qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH v2 4/6] qapi: add a text output visitor for pretty p


From: Daniel P. Berrange
Subject: [Qemu-devel] [PATCH v2 4/6] qapi: add a text output visitor for pretty printing types
Date: Fri, 9 Sep 2016 18:41:59 +0100

The current approach for pretty-printing QAPI types is to
convert them to JSON using the QMP output visitor and then
pretty-print the JSON document. This has an unfixable problem
that structs get their keys printed out in random order, since
JSON dicts do not contain any key ordering information.

To address this, introduce a text output visitor that can
directly pretty print a QAPI type into a string.

While this might sound superficially similar to the existing
string-output-visitor, it is formatting data in a completely
different way. The string-output-visitor only deals with
scalar values, or arrays of scalar values and formats the
values only into a single line, separated with commas. eg

    foo,bar,wizz

This new text-output-visitor deals with all arbitrarily
nested structs and arrays, and formats the names and value
into multi-line strings, so that previous example would
instead appear as

   foo
   bar
   wizz

Or, if a name was provided

   name: foo
   name: bar
   name: wizz

A more complex multi-level nested example is:

  name: hello
  num: 1729
  accounts:
      [0]:
          num: 1729
          name: hello
      [1]:
          num: 1729
          name: hello
      [2]:
          num: 1729
          name: hello
          info:
              help: world
      [3]:
          num: 1729
          name: hello
      [4]:
          num: 1729
          name: hello
          payments:
              [0]: 1729
              [1]: 1729
              [2]: 1729

As such there is no useful amount of code that can be shared
between the two output visitors and it thus is clearer to
read the code if they are kept separate. The docs for the
string-input-visitor are expanded to make the difference in
output formats clearer.

Signed-off-by: Daniel P. Berrange <address@hidden>
---
 include/qapi/string-output-visitor.h |  28 ++-
 include/qapi/text-output-visitor.h   |  77 ++++++++
 qapi/Makefile.objs                   |   1 +
 qapi/text-output-visitor.c           | 349 +++++++++++++++++++++++++++++++++
 tests/Makefile.include               |   5 +-
 tests/test-text-output-visitor.c     | 366 +++++++++++++++++++++++++++++++++++
 6 files changed, 823 insertions(+), 3 deletions(-)
 create mode 100644 include/qapi/text-output-visitor.h
 create mode 100644 qapi/text-output-visitor.c
 create mode 100644 tests/test-text-output-visitor.c

diff --git a/include/qapi/string-output-visitor.h 
b/include/qapi/string-output-visitor.h
index 268dfe9..920cd7a 100644
--- a/include/qapi/string-output-visitor.h
+++ b/include/qapi/string-output-visitor.h
@@ -21,14 +21,38 @@ typedef struct StringOutputVisitor StringOutputVisitor;
  * Create a new string output visitor.
  *
  * Using @human creates output that is a bit easier for humans to read
- * (for example, showing integer values in both decimal and hex).
+ * (for example, showing integer values in both decimal and hex). The
+ * output will only contain the values for the visited items, not the
+ * field names. When encountering lists of integers, order of the list
+ * elements will not be preserved in the output format. They will be
+ * re-arranged in numerical order and contiguous values merged into
+ * ranges. Strings will have double quotes added. If @human is set
+ * to true, then integers will be printed in both decimal and hexidecimal
+ * format. Some example outputs:
  *
- * If everything else succeeds, pass @result to visit_complete() to
+ * - Single integer (human friendly)
+ *    42 (0x2a)
+ *
+ * - List of integers
+ *   0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807
+ *
+ * - Boolean
+ *   true
+ *
+ * - Sring
+ *   "QEMU"
+ *
+ * If everything succeeds, pass @result to visit_complete() to
  * collect the result of the visit.
  *
  * The string output visitor does not implement support for visiting
  * QAPI structs, alternates, null, or arbitrary QTypes.  It also
  * requires a non-null list argument to visit_start_list().
+ *
+ * For outputting of complex types, including the field names, the
+ * TextOutputVisitor implementation is likely to be a better choice,
+ * as it can deal with arbitrary nesting and will preserve ordering
+ * of lists of integers.
  */
 Visitor *string_output_visitor_new(bool human, char **result);
 
diff --git a/include/qapi/text-output-visitor.h 
b/include/qapi/text-output-visitor.h
new file mode 100644
index 0000000..7996efb
--- /dev/null
+++ b/include/qapi/text-output-visitor.h
@@ -0,0 +1,77 @@
+/*
+ * Text pretty printing Visitor
+ *
+ * Copyright Red Hat, Inc. 2016
+ *
+ * Author: Daniel Berrange <address@hidden>
+ *
+ * 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 TEXT_OUTPUT_VISITOR_H
+#define TEXT_OUTPUT_VISITOR_H
+
+#include "qapi/visitor.h"
+
+typedef struct TextOutputVisitor TextOutputVisitor;
+
+/**
+ * text_output_visitor_new:
+ * @extraIndent: number of extra levels of indent to apply
+ * @skipLevel: skip output of nodes less than depth @skipLevel
+ *
+ * Create a new output visitor for displaying objects
+ * in a pretty-printed text format. The @extraIdent
+ * parameter can be used to add extra levels of whitespace
+ * indentation on the output text. If there are some nodes
+ * at the top level of the QAPI object that should not be
+ * displayed, the @skipLevel parameter can be set to a
+ * non-zero value to hide them.
+ *
+ * The objects are printed in a multi-line indented
+ * fashion, such that each line contains a single
+ * value. Extra indentation is added each time a
+ * compound type (list, struct) is entered.
+ *
+ * To obtain the formatted string, call visit_complete()
+ * passing a pointer to a "char *".
+ *
+ * <example>
+ *   <title>Print of a complex type</title>
+ *   <programlisting>
+ *  name: hello
+ *  num: 1729
+ *  accounts:
+ *      [0]:
+ *          num: 1729
+ *          name: hello
+ *      [1]:
+ *          num: 1729
+ *          name: hello
+ *      [2]:
+ *          num: 1729
+ *          name: hello
+ *          info:
+ *              help: world
+ *      [3]:
+ *          num: 1729
+ *          name: hello
+ *      [4]:
+ *          num: 1729
+ *          name: hello
+ *          payments:
+ *              [0]: 1729
+ *              [1]: 1729
+ *              [2]: 1729
+ *   </programlisting>
+ * </example>
+ *
+ * Returns: a pointer to new output visitor
+ */
+Visitor *text_output_visitor_new(int extraIndent,
+                                 int skipLevel);
+
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 7ea4aeb..e702f07 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -2,5 +2,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 qapi-clone-visitor.o
+util-obj-y += text-output-visitor.o
 util-obj-y += qmp-event.o
 util-obj-y += qapi-util.o
diff --git a/qapi/text-output-visitor.c b/qapi/text-output-visitor.c
new file mode 100644
index 0000000..6d499d0
--- /dev/null
+++ b/qapi/text-output-visitor.c
@@ -0,0 +1,349 @@
+/*
+ * Text pretty printing Visitor
+ *
+ * Copyright Red Hat, Inc. 2016
+ *
+ * Author: Daniel Berrange <address@hidden>
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu-common.h"
+#include "qapi/text-output-visitor.h"
+#include "qapi/visitor-impl.h"
+#include <math.h>
+
+struct TextOutputVisitorState {
+    bool isList;
+    size_t listIndex;
+
+    QSIMPLEQ_ENTRY(TextOutputVisitorState) next;
+};
+
+struct TextOutputVisitor {
+    Visitor visitor;
+    GString *string;
+    int level;
+    int skipLevel;
+    int extraIndent;
+
+    QSIMPLEQ_HEAD(TextOutputVisitorStateHead, TextOutputVisitorState) state;
+};
+
+#define INDENT (tov->extraIndent + ((tov->level - tov->skipLevel) * 4))
+
+static TextOutputVisitor *
+text_output_visitor_from_visitor(Visitor *v)
+{
+    return container_of(v, TextOutputVisitor, visitor);
+}
+
+
+static void
+text_output_visitor_open_compound_type(struct TextOutputVisitor *tov,
+                                       bool isList)
+{
+    struct TextOutputVisitorState *currstate = QSIMPLEQ_FIRST(&tov->state);
+    struct TextOutputVisitorState *state =
+        g_new0(struct TextOutputVisitorState, 1);
+
+    if (currstate && currstate->isList) {
+        g_string_append_printf(tov->string, "\n");
+    }
+    state->isList = isList;
+
+    QSIMPLEQ_INSERT_HEAD(&tov->state, state, next);
+
+    tov->level++;
+}
+
+
+static void
+text_output_visitor_close_compound_type(struct TextOutputVisitor *tov)
+{
+    struct TextOutputVisitorState *state = QSIMPLEQ_FIRST(&tov->state);
+
+    tov->level--;
+
+    QSIMPLEQ_REMOVE_HEAD(&tov->state, next);
+
+    g_free(state);
+}
+
+
+static void
+text_output_visitor_print_list_index(struct TextOutputVisitor *tov)
+{
+    struct TextOutputVisitorState *state = QSIMPLEQ_FIRST(&tov->state);
+
+    if (!state || !state->isList) {
+        return;
+    }
+
+    if (tov->level >= tov->skipLevel) {
+        g_string_append_printf(tov->string,
+                               "%*s[%zu]:",
+                               INDENT, "", state->listIndex++);
+    }
+}
+
+
+static char *
+text_output_visitor_format_name(const char *name)
+{
+    if (!name) {
+        return g_strdup("<anon>");
+    } else {
+        char *ret = g_strdup(name);
+        gsize i;
+        for (i = 0; ret[i]; i++) {
+            if (ret[i] == '-') {
+                ret[i] = ' ';
+            }
+        }
+        return ret;
+    }
+}
+
+static void
+text_output_visitor_print_scalar(TextOutputVisitor *tov,
+                                 const char *name,
+                                 const char *fmt,
+                                 ...)
+{
+    struct TextOutputVisitorState *state = QSIMPLEQ_FIRST(&tov->state);
+    va_list vargs;
+    char *val;
+    char *key;
+
+    va_start(vargs, fmt);
+
+    text_output_visitor_print_list_index(tov);
+
+    val = g_strdup_vprintf(fmt, vargs);
+
+    if ((!state && !name) ||
+        (state && state->isList)) {
+        g_string_append_printf(tov->string,
+                               !state ? "%s\n" : " %s\n", val);
+    } else {
+        key = text_output_visitor_format_name(name);
+        g_string_append_printf(tov->string,
+                               "%*s%s: %s\n",
+                               INDENT, "", key, val);
+        g_free(key);
+    }
+
+    g_free(val);
+    va_end(vargs);
+}
+
+
+static void
+text_output_visitor_print_type_int64(Visitor *v, const char *name, int64_t 
*obj,
+                                     Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+
+    if (tov->level < tov->skipLevel) {
+        return;
+    }
+
+    text_output_visitor_print_scalar(tov, name, "%" PRIu64, *obj);
+}
+
+
+static void
+text_output_visitor_print_type_uint64(Visitor *v, const char *name,
+                                      uint64_t *obj, Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+
+    if (tov->level < tov->skipLevel) {
+        return;
+    }
+
+    text_output_visitor_print_scalar(tov, name, "%" PRIi64, *obj);
+}
+
+
+static void
+text_output_visitor_print_type_size(Visitor *v, const char *name, uint64_t 
*obj,
+                                    Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+    char *szval;
+
+    if (tov->level < tov->skipLevel) {
+        return;
+    }
+
+    szval = qemu_szutostr_full(*obj, '\0', true, " ");
+    text_output_visitor_print_scalar(tov, name, "%" PRIu64 " (%s)",
+                                     *obj, szval);
+    g_free(szval);
+}
+
+
+static void
+text_output_visitor_print_type_bool(Visitor *v, const char *name, bool *obj,
+                                    Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+
+    if (tov->level < tov->skipLevel) {
+        return;
+    }
+
+    text_output_visitor_print_scalar(tov, name, "%s", *obj ? "true" : "false");
+}
+
+
+static void
+text_output_visitor_print_type_str(Visitor *v, const char *name, char **obj,
+                                   Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+
+    if (tov->level < tov->skipLevel) {
+        return;
+    }
+
+    text_output_visitor_print_scalar(tov, name, "%s", *obj ? *obj : "<null>");
+}
+
+
+static void
+text_output_visitor_print_type_number(Visitor *v, const char *name, double 
*obj,
+                                      Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+
+    if (tov->level < tov->skipLevel) {
+        return;
+    }
+
+    text_output_visitor_print_scalar(tov, name, "%f", *obj);
+}
+
+
+static void
+text_output_visitor_start_list(Visitor *v, const char *name, GenericList 
**list,
+                               size_t size, Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+    char *key;
+
+    if (tov->level >= tov->skipLevel && name) {
+        key = text_output_visitor_format_name(name);
+        g_string_append_printf(tov->string,
+                               "%*s%s:\n",
+                               INDENT, "",
+                               key);
+        g_free(key);
+    }
+    text_output_visitor_open_compound_type(tov, true);
+}
+
+
+static GenericList *
+text_output_visitor_next_list(Visitor *v, GenericList *tail, size_t size)
+{
+    GenericList *ret = tail->next;
+    return ret;
+}
+
+
+static void
+text_output_visitor_end_list(Visitor *v, void **obj)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+
+    text_output_visitor_close_compound_type(tov);
+}
+
+
+static void
+text_output_visitor_start_struct(Visitor *v, const char *name, void **obj,
+                                 size_t size, Error **errp)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+    struct TextOutputVisitorState *state = QSIMPLEQ_FIRST(&tov->state);
+
+    if (tov->level >= tov->skipLevel && name &&
+        state && !state->isList) {
+        g_string_append_printf(tov->string,
+                               "%*s%s:\n",
+                               INDENT, "",
+                               name);
+    }
+    text_output_visitor_print_list_index(tov);
+    text_output_visitor_open_compound_type(tov, false);
+}
+
+
+static void
+text_output_visitor_end_struct(Visitor *v, void **obj)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+    text_output_visitor_close_compound_type(tov);
+}
+
+
+static void
+text_output_visitor_complete(Visitor *v, void *opaque)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+    char **result = opaque;
+
+    *result = g_string_free(tov->string, false);
+    tov->string = NULL;
+}
+
+
+static void
+text_output_visitor_free(Visitor *v)
+{
+    TextOutputVisitor *tov = text_output_visitor_from_visitor(v);
+
+    if (tov->string) {
+        g_string_free(tov->string, true);
+    }
+    g_free(tov);
+}
+
+
+Visitor *
+text_output_visitor_new(int extraIndent,
+                        int skipLevel)
+{
+    TextOutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->extraIndent = extraIndent;
+    v->skipLevel = skipLevel;
+    v->string = g_string_new(NULL);
+    v->visitor.type = VISITOR_OUTPUT;
+    v->visitor.type_int64 = text_output_visitor_print_type_int64;
+    v->visitor.type_uint64 = text_output_visitor_print_type_uint64;
+    v->visitor.type_size = text_output_visitor_print_type_size;
+    v->visitor.type_bool = text_output_visitor_print_type_bool;
+    v->visitor.type_str = text_output_visitor_print_type_str;
+    v->visitor.type_number = text_output_visitor_print_type_number;
+    v->visitor.start_list = text_output_visitor_start_list;
+    v->visitor.next_list = text_output_visitor_next_list;
+    v->visitor.end_list = text_output_visitor_end_list;
+    v->visitor.start_struct = text_output_visitor_start_struct;
+    v->visitor.end_struct = text_output_visitor_end_struct;
+    v->visitor.complete = text_output_visitor_complete;
+    v->visitor.free = text_output_visitor_free;
+
+    QSIMPLEQ_INIT(&v->state);
+
+    return &v->visitor;
+}
diff --git a/tests/Makefile.include b/tests/Makefile.include
index e3a3266..aa1188f 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -33,6 +33,8 @@ check-unit-y += tests/test-string-input-visitor$(EXESUF)
 gcov-files-test-string-input-visitor-y = qapi/string-input-visitor.c
 check-unit-y += tests/test-string-output-visitor$(EXESUF)
 gcov-files-test-string-output-visitor-y = qapi/string-output-visitor.c
+check-unit-y += tests/test-text-output-visitor$(EXESUF)
+gcov-files-test-text-output-visitor-y = qapi/text-output-visitor.c
 check-unit-y += tests/test-qmp-event$(EXESUF)
 gcov-files-test-qmp-event-y += qapi/qmp-event.c
 check-unit-y += tests/test-opts-visitor$(EXESUF)
@@ -433,7 +435,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o 
tests/check-qdict.o \
        tests/check-qjson.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-clone-visitor.o \
+       tests/test-clone-visitor.o tests/test-text-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 \
@@ -530,6 +532,7 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json 
$(SRC_PATH)/scripts/qapi-int
 
 tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o 
$(test-qapi-obj-y)
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o 
$(test-qapi-obj-y)
+tests/test-text-output-visitor$(EXESUF): tests/test-text-output-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-clone-visitor$(EXESUF): tests/test-clone-visitor.o 
$(test-qapi-obj-y)
diff --git a/tests/test-text-output-visitor.c b/tests/test-text-output-visitor.c
new file mode 100644
index 0000000..6382732
--- /dev/null
+++ b/tests/test-text-output-visitor.c
@@ -0,0 +1,366 @@
+/*
+ * String Output Visitor unit-tests.
+ *
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Paolo Bonzini <address@hidden> (based on test-qmp-output-visitor)
+ *
+ * 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 "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qapi/text-output-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qapi/qmp/types.h"
+
+
+static void test_visitor_out_int(void)
+{
+    int64_t value = 42;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    visit_type_int(v, NULL, &value, &error_abort);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==, "42\n");
+    g_free(str);
+    visit_free(v);
+}
+
+
+static void test_visitor_out_size(void)
+{
+    uint64_t value = 1729;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    visit_type_size(v, NULL, &value, &error_abort);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==, "1729 (1.69 KiB)\n");
+    g_free(str);
+    visit_free(v);
+}
+
+static void test_visitor_out_intList(void)
+{
+    int64_t value[] = {0, 1, 9, 10, 16, 15, 14,
+        3, 4, 5, 6, 11, 12, 13, 21, 22, INT64_MAX - 1, INT64_MAX};
+    intList *list = NULL, **tmp = &list;
+    int i;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    for (i = 0; i < sizeof(value) / sizeof(value[0]); i++) {
+        *tmp = g_malloc0(sizeof(**tmp));
+        (*tmp)->value = value[i];
+        tmp = &(*tmp)->next;
+    }
+
+    visit_type_intList(v, NULL, &list, &error_abort);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==,
+                    "    [0]: 0\n"
+                    "    [1]: 1\n"
+                    "    [2]: 9\n"
+                    "    [3]: 10\n"
+                    "    [4]: 16\n"
+                    "    [5]: 15\n"
+                    "    [6]: 14\n"
+                    "    [7]: 3\n"
+                    "    [8]: 4\n"
+                    "    [9]: 5\n"
+                    "    [10]: 6\n"
+                    "    [11]: 11\n"
+                    "    [12]: 12\n"
+                    "    [13]: 13\n"
+                    "    [14]: 21\n"
+                    "    [15]: 22\n"
+                    "    [16]: 9223372036854775806\n"
+                    "    [17]: 9223372036854775807\n");
+    qapi_free_intList(list);
+    g_free(str);
+    visit_free(v);
+}
+
+static void test_visitor_out_bool(void)
+{
+    bool value = true;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    visit_type_bool(v, NULL, &value, &error_abort);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==, "true\n");
+    g_free(str);
+    visit_free(v);
+}
+
+static void test_visitor_out_number(void)
+{
+    double value = 3.14;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    visit_type_number(v, NULL, &value, &error_abort);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==, "3.140000\n");
+    g_free(str);
+    visit_free(v);
+}
+
+static void test_visitor_out_string(void)
+{
+    const char *string = "Q E M U";
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    visit_type_str(v, NULL, (char **)&string, &error_abort);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==, "Q E M U\n");
+    g_free(str);
+    visit_free(v);
+}
+
+static void test_visitor_out_no_string(void)
+{
+    char *string = NULL;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    /* A null string should return "" */
+    visit_type_str(v, NULL, &string, &error_abort);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==, "<null>\n");
+    g_free(str);
+    visit_free(v);
+}
+
+static void test_visitor_out_enum(void)
+{
+    char *actual, *expected;
+    EnumOne i;
+    Visitor *v;
+
+    for (i = 0; i < ENUM_ONE__MAX; i++) {
+        v = text_output_visitor_new(0, 0);
+        g_assert(v);
+
+        visit_type_EnumOne(v, "val", &i, &error_abort);
+
+        visit_complete(v, &actual);
+        expected = g_strdup_printf("val: %s\n", EnumOne_lookup[i]);
+
+        g_assert_cmpstr(actual, ==, expected);
+        g_free(expected);
+        g_free(actual);
+        visit_free(v);
+    }
+}
+
+static void test_visitor_out_enum_errors(void)
+{
+    EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 };
+    Visitor *v;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+        v = text_output_visitor_new(0, 0);
+        g_assert(v);
+
+        Error *err = NULL;
+        visit_type_EnumOne(v, "unused", &bad_values[i], &err);
+        error_free_or_abort(&err);
+        visit_free(v);
+    }
+}
+
+
+static void test_visitor_out_struct_named(void)
+{
+    const char *string = "hello";
+    int64_t i = 1729;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    visit_start_struct(v, NULL, NULL, 0, &error_abort);
+
+    visit_type_str(v, "name", (char **)&string, &error_abort);
+
+    visit_type_int(v, "num", &i, &error_abort);
+
+    visit_end_struct(v, NULL);
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==,
+                    "    name: hello\n"
+                    "    num: 1729\n");
+    g_free(str);
+    visit_free(v);
+}
+
+
+static void test_visitor_out_struct_anon(void)
+{
+    const char *string = "hello";
+    int64_t i = 1729;
+    char *str;
+    Visitor *v;
+
+    v = text_output_visitor_new(0, 1);
+    g_assert(v);
+
+    visit_start_struct(v, NULL, NULL, 0, &error_abort);
+
+    visit_type_str(v, NULL, (char **)&string, &error_abort);
+
+    visit_type_int(v, NULL, &i, &error_abort);
+
+    visit_end_struct(v, NULL);
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==,
+                    "<anon>: hello\n"
+                    "<anon>: 1729\n");
+    g_free(str);
+    visit_free(v);
+}
+
+
+static void test_visitor_out_complex(void)
+{
+    const char *string = "hello";
+    const char *string2 = "world";
+    int64_t n = 1729;
+    char *str;
+    Visitor *v;
+    gsize i;
+
+    v = text_output_visitor_new(0, 0);
+    g_assert(v);
+
+    visit_type_str(v, "full-name", (char **)&string, &error_abort);
+
+    visit_type_int(v, "num", &n, &error_abort);
+
+    visit_start_list(v, "accounts", NULL, 0, &error_abort);
+
+    for (i = 0; i < 5; i++) {
+        visit_start_struct(v, "account", NULL, 0, &error_abort);
+
+        visit_type_int(v, "num", &n, &error_abort);
+        visit_type_str(v, "name", (char **)&string, &error_abort);
+
+        if (i == 2) {
+            visit_start_struct(v, "info", NULL, 0, &error_abort);
+            visit_type_str(v, "help", (char **)&string2, &error_abort);
+            visit_end_struct(v, NULL);
+        } else if (i == 4) {
+            visit_start_list(v, "payment-info", NULL, 0, &error_abort);
+            visit_type_int(v, "num", &n, &error_abort);
+            visit_type_int(v, "num", &n, &error_abort);
+            visit_type_int(v, "num", &n, &error_abort);
+            visit_end_list(v, NULL);
+        }
+
+        visit_end_struct(v, NULL);
+    }
+
+    visit_end_list(v, NULL);
+
+    visit_complete(v, &str);
+    g_assert_cmpstr(str, ==,
+                    "full name: hello\n"
+                    "num: 1729\n"
+                    "accounts:\n"
+                    "    [0]:\n"
+                    "        num: 1729\n"
+                    "        name: hello\n"
+                    "    [1]:\n"
+                    "        num: 1729\n"
+                    "        name: hello\n"
+                    "    [2]:\n"
+                    "        num: 1729\n"
+                    "        name: hello\n"
+                    "        info:\n"
+                    "            help: world\n"
+                    "    [3]:\n"
+                    "        num: 1729\n"
+                    "        name: hello\n"
+                    "    [4]:\n"
+                    "        num: 1729\n"
+                    "        name: hello\n"
+                    "        payment info:\n"
+                    "            [0]: 1729\n"
+                    "            [1]: 1729\n"
+                    "            [2]: 1729\n");
+    g_free(str);
+    visit_free(v);
+}
+
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/string-visitor/output/int",
+                    test_visitor_out_int);
+    g_test_add_func("/string-visitor/output/size",
+                    test_visitor_out_size);
+    g_test_add_func("/string-visitor/output/bool",
+                    test_visitor_out_bool);
+    g_test_add_func("/string-visitor/output/number",
+                    test_visitor_out_number);
+    g_test_add_func("/string-visitor/output/string",
+                    test_visitor_out_string);
+    g_test_add_func("/string-visitor/output/no-string",
+                    test_visitor_out_no_string);
+    g_test_add_func("/string-visitor/output/enum",
+                    test_visitor_out_enum);
+    g_test_add_func("/string-visitor/output/enum-errors",
+                    test_visitor_out_enum_errors);
+    g_test_add_func("/string-visitor/output/intList",
+                    test_visitor_out_intList);
+    g_test_add_func("/string-visitor/output/struct-named",
+                    test_visitor_out_struct_named);
+    g_test_add_func("/string-visitor/output/struct-anon",
+                    test_visitor_out_struct_anon);
+    g_test_add_func("/string-visitor/output/complex",
+                    test_visitor_out_complex);
+    g_test_run();
+
+    return 0;
+}
-- 
2.7.4




reply via email to

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