Patchwork [v4,9/9] ASN.1 specific test cases

login
register
mail settings
Submitter Stefan Berger
Date March 21, 2013, 6:29 p.m.
Message ID <1363890571-15146-10-git-send-email-stefanb@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/229807/
State New
Headers show

Comments

Stefan Berger - March 21, 2013, 6:29 p.m.
BER visitor tests give us some assurance that the BER visitor
code works, and also end up by extention helping out on our
code coverage of the filesystem tests.
After the output visitor invocation the resuling buffer is
compared against a known byte stream -- this will lock the
implementation into producing specific byte arrays.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
 tests/Makefile           |   9 +
 tests/test-ber-visitor.c | 746 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 755 insertions(+)
 create mode 100644 tests/test-ber-visitor.c
Eric Blake - March 21, 2013, 10:05 p.m.
On 03/21/2013 12:29 PM, Stefan Berger wrote:
> BER visitor tests give us some assurance that the BER visitor
> code works, and also end up by extention helping out on our

s/extention/extension/

> code coverage of the filesystem tests.
> After the output visitor invocation the resuling buffer is

s/resuling/resulting/

> compared against a known byte stream -- this will lock the
> implementation into producing specific byte arrays.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
> ---
>  tests/Makefile           |   9 +
>  tests/test-ber-visitor.c | 746 +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 755 insertions(+)
>  create mode 100644 tests/test-ber-visitor.c
> 

> +tests/test-ber-visitor.o: $(addprefix include/qapi/, ber.h ber-input-visitor.h ber-output-visitor.h) $(addprefix qapi/, ber-common.c ber-input-visitor.c ber-output-visitor.c)
> +tests/test-ber-visitor$(EXESUF): tests/test-ber-visitor.o $(tools-obj-y) qapi/ber-output-visitor.o qapi/ber-input-visitor.o qapi/ber-common.o $(block-obj-y) libqemuutil.a libqemustub.a

Long lines - worth using backslash-newline continuation?

> +++ b/tests/test-ber-visitor.c
> @@ -0,0 +1,746 @@
> +/*
> + * BER Output Visitor unit-tests.
> + *
> + * Copyright (C) 2011 Red Hat Inc.
> + * Copyright (C) 2011 IBM Corporation

It's 2013 (probably applies to other files earlier in the series, as well).

> +static void test_visitor_out_string(TestInputOutputVisitor *data,
> +                                    const void *unused)
> +{
> +    char *string_in = (char *) "Q E M U", *string_out = NULL;

Does the fact that you have to cast here...

> +    Error *errp = NULL;
> +
> +    visit_type_str(data->ov, &string_in, NULL, &errp);

...indicate a lack of const-correctness on visit_type_str()?
Stefan Berger - March 22, 2013, 5:07 p.m.
On 03/21/2013 06:05 PM, Eric Blake wrote:
> On 03/21/2013 12:29 PM, Stefan Berger wrote:
>> +tests/test-ber-visitor.o: $(addprefix include/qapi/, ber.h ber-input-visitor.h ber-output-visitor.h) $(addprefix qapi/, ber-common.c ber-input-visitor.c ber-output-visitor.c)
>> +tests/test-ber-visitor$(EXESUF): tests/test-ber-visitor.o $(tools-obj-y) qapi/ber-output-visitor.o qapi/ber-input-visitor.o qapi/ber-common.o $(block-obj-y) libqemuutil.a libqemustub.a
> Long lines - worth using backslash-newline continuation?

Yes. Others are long but these lines are now the longest.

>
>> +++ b/tests/test-ber-visitor.c
>> @@ -0,0 +1,746 @@
>> +/*
>> + * BER Output Visitor unit-tests.
>> + *
>> + * Copyright (C) 2011 Red Hat Inc.
>> + * Copyright (C) 2011 IBM Corporation
> It's 2013 (probably applies to other files earlier in the series, as well).
>
>> +static void test_visitor_out_string(TestInputOutputVisitor *data,
>> +                                    const void *unused)
>> +{
>> +    char *string_in = (char *) "Q E M U", *string_out = NULL;
> Does the fact that you have to cast here...
>
>> +    Error *errp = NULL;
>> +
>> +    visit_type_str(data->ov, &string_in, NULL, &errp);
> ...indicate a lack of const-correctness on visit_type_str()?
>

The visitor interface is used for serialization and de-serialization. 
Upon de-serialization the underlying visitor can allocate memory for the 
string it found while decoding, so you can pass in a pointer to a NULL 
pointer and will get a valid pointer to the string back.

    Stefan

Patch

diff --git a/tests/Makefile b/tests/Makefile
index 578d732..b74f90f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -23,6 +23,10 @@  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-ber-visitor$(EXESUF)
+gcov-files-test-vistor-y = qapi/ber-input-visitor.c
+
+
 check-unit-y += tests/test-coroutine$(EXESUF)
 ifeq ($(CONFIG_WIN32),y)
 gcov-files-test-coroutine-y = coroutine-win32.c
@@ -134,6 +138,11 @@  tests/fdc-test$(EXESUF): tests/fdc-test.o
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/tmp105-test$(EXESUF): tests/tmp105-test.o
 
+tests/test-ber-visitor.o : QEMU_CFLAGS += -I include/qapi
+
+tests/test-ber-visitor.o: $(addprefix include/qapi/, ber.h ber-input-visitor.h ber-output-visitor.h) $(addprefix qapi/, ber-common.c ber-input-visitor.c ber-output-visitor.c)
+tests/test-ber-visitor$(EXESUF): tests/test-ber-visitor.o $(tools-obj-y) qapi/ber-output-visitor.o qapi/ber-input-visitor.o qapi/ber-common.o $(block-obj-y) libqemuutil.a libqemustub.a
+
 # QTest rules
 
 TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
diff --git a/tests/test-ber-visitor.c b/tests/test-ber-visitor.c
new file mode 100644
index 0000000..f309432
--- /dev/null
+++ b/tests/test-ber-visitor.c
@@ -0,0 +1,746 @@ 
+/*
+ * BER Output Visitor unit-tests.
+ *
+ * Copyright (C) 2011 Red Hat Inc.
+ * Copyright (C) 2011 IBM Corporation
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *  Stefan Berger <stefanb@linux.vnet.ibm.com>
+ *
+ * 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/ber-output-visitor.h"
+#include "qapi/ber-input-visitor.h"
+#include "hw/hw.h"
+#include "include/qapi/visitor.h"
+
+
+typedef struct TestExpResult {
+    const uint8_t *exp;
+    size_t exp_len;
+} TestExpResult;
+
+typedef struct TestInputOutputVisitorData {
+    QEMUFile *qoutfile;
+    BEROutputVisitor *bov;
+    Visitor *ov;
+
+    QEMUFile *qinfile;
+    BERInputVisitor *biv;
+    Visitor *iv;
+
+    const TestExpResult *ter;
+} TestInputOutputVisitor;
+
+static void test_visitor_dump(const uint8_t *exp, size_t exp_len,
+                              const uint8_t *act, size_t act_len)
+{
+    size_t i;
+
+    fprintf(stderr, "\nExpected output:");
+
+    for (i = 0; i < exp_len; i++) {
+        if ((i % 0x8) == 0) {
+            fprintf(stderr, "\n    ");
+        }
+        fprintf(stderr, "0x%02x, ", exp[i]);
+    }
+
+    fprintf(stderr, "\nActual output:");
+
+    for (i = 0; i < act_len; i++) {
+        if ((i % 0x8) == 0) {
+            fprintf(stderr, "\n    ");
+        }
+        fprintf(stderr, "0x%02x, ", act[i]);
+    }
+    fprintf(stderr, "\n");
+}
+
+static void test_visitor_check_result(const uint8_t *exp, size_t exp_len,
+                                      const uint8_t *act, size_t act_len)
+{
+    size_t i;
+
+    if (exp_len != act_len) {
+        test_visitor_dump(exp, exp_len, act, act_len);
+    }
+
+    for (i = 0; i < exp_len; i++) {
+        if (exp[i] != act[i]) {
+            test_visitor_dump(exp, exp_len, act, act_len);
+        }
+    }
+}
+
+static void visitor_output_setup(TestInputOutputVisitor *data,
+                                 BERTypePC ber_type_pc,
+                                 const TestExpResult *ter)
+{
+    data->ter = ter;
+
+    data->qoutfile = qemu_bufopen("w", NULL);
+
+    data->bov = ber_output_visitor_new(data->qoutfile, ber_type_pc);
+    g_assert(data->bov != NULL);
+
+    data->ov = ber_output_get_visitor(data->bov);
+    g_assert(data->ov != NULL);
+}
+
+static void visitor_output_setup_primitive(TestInputOutputVisitor *data,
+                                           const void *results)
+{
+    const TestExpResult *ter = results;
+
+    visitor_output_setup(data, BER_TYPE_PRIMITIVE, ter);
+}
+
+static void visitor_output_setup_constructed(TestInputOutputVisitor *data,
+                                             const void *results)
+{
+    const TestExpResult *ter = results;
+
+    visitor_output_setup(data, BER_TYPE_CONSTRUCTED, ter);
+}
+
+static void visitor_input_setup(TestInputOutputVisitor *data)
+{
+    const QEMUSizedBuffer *qsb = qemu_buf_get(data->qoutfile);
+    QEMUSizedBuffer *new_qsb = qsb_clone(qsb);
+    unsigned char *buffer = NULL;
+    g_assert(new_qsb != NULL);
+
+    qsb_get_buffer(qsb, 0, qsb_get_length(qsb), &buffer);
+    test_visitor_check_result(data->ter->exp, data->ter->exp_len,
+                              buffer, qsb_get_length(qsb));
+    g_free(buffer);
+
+    data->qinfile = qemu_bufopen("r", new_qsb);
+    g_assert(data->qinfile != NULL);
+
+    data->biv = ber_input_visitor_new(data->qinfile, ~0);
+    g_assert(data->biv != NULL);
+
+    data->iv = ber_input_get_visitor(data->biv);
+    g_assert(data->iv != NULL);
+}
+
+static void visitor_output_teardown(TestInputOutputVisitor *data,
+                                    const void *unused)
+{
+    ber_output_visitor_cleanup(data->bov);
+    data->bov = NULL;
+    data->ov = NULL;
+
+    ber_input_visitor_cleanup(data->biv);
+    data->biv = NULL;
+    data->iv = NULL;
+
+    if (data->qinfile) {
+        qemu_fclose(data->qinfile);
+    }
+    if (data->qoutfile) {
+        qemu_fclose(data->qoutfile);
+    }
+}
+
+static const uint8_t test_visitor_int_exp[] = {
+    0x42, 0x01, 0xd6,
+};
+
+static void test_visitor_out_int(TestInputOutputVisitor *data,
+                                 const void *unused)
+{
+    int64_t value_in = -42, value_out = 0;
+    Error *errp = NULL;
+
+    visit_type_int(data->ov, &value_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    visitor_input_setup(data);
+
+    visit_type_int(data->iv, &value_out, NULL, &errp);
+    g_assert_cmpint(value_out, ==, -42);
+}
+
+static const uint8_t test_visitor_boolean_exp[] = {
+    0x01, 0x01, 0x01,
+};
+
+static void test_visitor_out_boolean(TestInputOutputVisitor *data,
+                                     const void *unused)
+{
+    Error *errp = NULL;
+    bool value_in = true, value_out = false;
+
+    visit_type_bool(data->ov, &value_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    visitor_input_setup(data);
+
+    visit_type_bool(data->iv, &value_out, NULL, &errp);
+    g_assert_cmpint(value_out, ==, true);
+
+}
+
+static const uint8_t test_visitor_string_exp[] = {
+    0x16, 0x07, 0x51, 0x20, 0x45, 0x20, 0x4d, 0x20,
+    0x55,
+};
+
+static void test_visitor_out_string(TestInputOutputVisitor *data,
+                                    const void *unused)
+{
+    char *string_in = (char *) "Q E M U", *string_out = NULL;
+    Error *errp = NULL;
+
+    visit_type_str(data->ov, &string_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    visitor_input_setup(data);
+
+    visit_type_str(data->iv, &string_out, NULL, &errp);
+
+    g_assert_cmpstr(string_out, ==, string_in);
+    g_free(string_out);
+}
+
+static const uint8_t test_visitor_no_string_exp[] = {
+    0x16, 0x00,
+};
+
+static void test_visitor_out_no_string(TestInputOutputVisitor *data,
+                                       const void *unused)
+{
+    char *string_in = NULL, *string_out = NULL;
+    Error *errp = NULL;
+
+    /* A null string should return "" */
+    visit_type_str(data->ov, &string_in, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+    g_assert(qsb_get_length(qemu_buf_get(data->qoutfile)) == 2);
+
+    visitor_input_setup(data);
+
+    visit_type_str(data->iv, &string_out, NULL, &errp);
+    g_assert_cmpstr(string_out, ==, "");
+
+    g_free(string_out);
+}
+
+#define SIMPLE_STRUCT_ARRAY_SIZE 5
+
+typedef struct SimpleStruct {
+    int64_t integer;
+    bool boolean;
+    char *string;
+    uint32_t data[SIMPLE_STRUCT_ARRAY_SIZE];
+    uint16_t *extdata;
+} SimpleStruct;
+
+static void visit_type_SimpleStruct(Visitor *v, SimpleStruct **obj,
+                                    const char *name, Error **errp)
+{
+    uint32_t *data;
+    uint16_t *extdata;
+    visit_start_struct(v, (void **)obj, "SimpleStruct", name,
+                       sizeof(SimpleStruct), errp);
+
+    visit_type_int(v, &(*obj)->integer, "integer", errp);
+    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
+    visit_type_str(v, &(*obj)->string, "string", errp);
+
+    data = (uint32_t *)&(*obj)->data;
+
+    visit_type_sized_buffer(v, (void **)&data, "data",
+                            SIMPLE_STRUCT_ARRAY_SIZE, sizeof(uint32_t), errp);
+
+    extdata = (uint16_t *)(*obj)->extdata;
+
+    visit_type_sized_buffer(v, (void **)&extdata, "extdata",
+                            SIMPLE_STRUCT_ARRAY_SIZE, sizeof(uint16_t), errp);
+
+    (*obj)->extdata = extdata;
+
+    visit_end_struct(v, errp);
+}
+
+static const uint8_t test_visitor_struct_exp_cons[] = {
+    0x30, 0x80, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x00,
+    0x16, 0x03, 0x66, 0x6f, 0x6f, 0x04, 0x14, 0x00,
+    0x00, 0x11, 0x11, 0x00, 0x00, 0x22, 0x22, 0x00,
+    0x00, 0x33, 0x33, 0x00, 0x00, 0x44, 0x44, 0x00,
+    0x00, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x01, 0x00,
+    0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00,
+    0x00,
+};
+
+static const uint8_t test_visitor_struct_exp_prim[] = {
+    0x30, 0x2d, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x00,
+    0x16, 0x03, 0x66, 0x6f, 0x6f, 0x04, 0x14, 0x00,
+    0x00, 0x11, 0x11, 0x00, 0x00, 0x22, 0x22, 0x00,
+    0x00, 0x33, 0x33, 0x00, 0x00, 0x44, 0x44, 0x00,
+    0x00, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x01, 0x00,
+    0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05,
+};
+
+
+static void test_visitor_out_struct(TestInputOutputVisitor *data,
+                                    const void *unused)
+{
+    uint16_t extdata[] = {1, 2, 3, 4, 5};
+    SimpleStruct test_struct = {
+        .integer = 42,
+        .boolean = false,
+        .string = (char *)"foo",
+        .data = {
+            0x1111, 0x2222, 0x3333, 0x4444, 0x5555
+        },
+        .extdata = extdata,
+    };
+    SimpleStruct *p_in = &test_struct, *p_out = NULL;
+    Error *errp = NULL;
+    int i;
+
+    visit_type_SimpleStruct(data->ov, &p_in, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_SimpleStruct(data->iv, &p_out, NULL, &errp);
+
+    g_assert_cmpint(p_out->integer, ==, 42);
+    g_assert_cmpint(p_out->boolean, ==, 0);
+    g_assert_cmpstr(p_out->string, ==, "foo");
+
+    for (i = 0; i < ARRAY_SIZE(test_struct.data); i++) {
+        g_assert_cmpint(p_out->data[i], ==, (i + 1) * 0x1111);
+    }
+
+    for (i = 0; i < ARRAY_SIZE(extdata); i++) {
+        g_assert_cmpint(p_out->extdata[i], ==, (i + 1));
+    }
+
+    g_free(p_out->extdata);
+    g_free(p_out->string);
+    g_free(p_out);
+}
+
+typedef struct NestedStruct {
+    int64_t integer;
+    bool boolean;
+    SimpleStruct simpleStruct[2];
+    char *string;
+} NestedStruct;
+
+static void visit_type_NestedStruct(Visitor *v, NestedStruct **obj,
+                                    const char *name, Error **errp)
+{
+    unsigned int i;
+
+    visit_start_struct(v, (void **)obj, "NestedStruct", name,
+                       sizeof(NestedStruct), errp);
+
+    visit_type_int(v, &(*obj)->integer, "integer", errp);
+    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
+
+    for (i = 0; i < ARRAY_SIZE((*obj)->simpleStruct); i++) {
+        SimpleStruct *simple = &(*obj)->simpleStruct[i];
+        visit_type_SimpleStruct(v, &simple, "SimpleStruct", errp);
+    }
+
+    visit_type_str(v, &(*obj)->string, "string", errp);
+
+    visit_end_struct(v, errp);
+}
+
+static const uint8_t test_visitor_nested_struct_exp_cons[] = {
+    0x30, 0x80, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x01,
+    0x30, 0x80, 0x42, 0x02, 0x03, 0xe8, 0x01, 0x01,
+    0x00, 0x16, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
+    0x04, 0x14, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00,
+    0x22, 0x22, 0x00, 0x00, 0x33, 0x33, 0x00, 0x00,
+    0x44, 0x44, 0x00, 0x00, 0x55, 0x55, 0x04, 0x0a,
+    0x00, 0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x02,
+    0x00, 0x01, 0x00, 0x00, 0x30, 0x80, 0x42, 0x02,
+    0x07, 0xd0, 0x01, 0x01, 0x00, 0x16, 0x06, 0x77,
+    0x6f, 0x72, 0x6c, 0x64, 0x21, 0x04, 0x14, 0x11,
+    0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33,
+    0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55,
+    0x55, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x55, 0x00,
+    0x44, 0x00, 0x33, 0x00, 0x22, 0x00, 0x11, 0x00,
+    0x00, 0x16, 0x03, 0x62, 0x61, 0x72, 0x00, 0x00,
+};
+
+static const uint8_t test_visitor_nested_struct_exp_prim[] = {
+    0x30, 0x70, 0x42, 0x01, 0x2a, 0x01, 0x01, 0x01,
+    0x30, 0x30, 0x42, 0x02, 0x03, 0xe8, 0x01, 0x01,
+    0x00, 0x16, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
+    0x04, 0x14, 0x00, 0x00, 0x11, 0x11, 0x00, 0x00,
+    0x22, 0x22, 0x00, 0x00, 0x33, 0x33, 0x00, 0x00,
+    0x44, 0x44, 0x00, 0x00, 0x55, 0x55, 0x04, 0x0a,
+    0x00, 0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x02,
+    0x00, 0x01, 0x30, 0x31, 0x42, 0x02, 0x07, 0xd0,
+    0x01, 0x01, 0x00, 0x16, 0x06, 0x77, 0x6f, 0x72,
+    0x6c, 0x64, 0x21, 0x04, 0x14, 0x11, 0x11, 0x11,
+    0x11, 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33,
+    0x33, 0x44, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x55, 0x00, 0x44, 0x00,
+    0x33, 0x00, 0x22, 0x00, 0x11, 0x16, 0x03, 0x62,
+    0x61, 0x72,
+};
+
+static void test_visitor_out_nested_struct(TestInputOutputVisitor *data,
+                                           const void *unused)
+{
+    uint16_t extdata1[] = { 5, 4, 3, 2, 1};
+    uint16_t extdata2[] = { 0x55, 0x44, 0x33, 0x22, 0x11};
+    NestedStruct nested_struct = {
+        .integer = 42,
+        .boolean = true,
+        .simpleStruct = {
+            {
+                .integer = 1000,
+                .boolean = false,
+                .string = (char *)"Hello",
+                .data = {
+                    0x1111, 0x2222, 0x3333, 0x4444, 0x5555
+                },
+                .extdata = extdata1,
+            }, {
+                .integer = 2000,
+                .boolean = false,
+                .string = (char *)"world!",
+                .data = {
+                    0x11111111, 0x22222222, 0x33333333, 0x44444444,
+                    0x55555555
+                },
+                .extdata = extdata2,
+            },
+        },
+        .string = (char *)"bar",
+    };
+    NestedStruct *p_in = &nested_struct, *p_out = NULL;
+    Error *errp = NULL;
+    unsigned int i;
+
+    visit_type_NestedStruct(data->ov, &p_in, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_NestedStruct(data->iv, &p_out, NULL, &errp);
+
+    g_assert_cmpint(p_out->integer, ==, 42);
+    g_assert_cmpint(p_out->boolean, ==, true);
+
+    g_assert_cmpint(p_out->simpleStruct[0].integer, ==, 1000);
+    g_assert_cmpint(p_out->simpleStruct[0].boolean, ==, 0);
+    g_assert_cmpstr(p_out->simpleStruct[0].string , ==, "Hello");
+    for (i = 0; i < ARRAY_SIZE(nested_struct.simpleStruct[0].data); i++) {
+        g_assert_cmpint(p_out->simpleStruct[0].data[i], ==, (i + 1) * 0x1111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata1); i++) {
+        g_assert_cmpint(p_out->simpleStruct[0].extdata[i], ==, (5 - i));
+    }
+
+    g_assert_cmpint(p_out->simpleStruct[1].integer, ==, 2000);
+    g_assert_cmpint(p_out->simpleStruct[1].boolean, ==, 0);
+    g_assert_cmpstr(p_out->simpleStruct[1].string , ==, "world!");
+    for (i = 0; i < ARRAY_SIZE(nested_struct.simpleStruct[1].data); i++) {
+        g_assert_cmpint(p_out->simpleStruct[1].data[i], ==,
+                        (i + 1) * 0x11111111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata2); i++) {
+        g_assert_cmpint(p_out->simpleStruct[1].extdata[i], ==, (5 - i) * 0x11);
+    }
+
+    g_assert_cmpstr(p_out->string, ==, "bar");
+
+    g_free(p_out->string);
+    for (i = 0; i < ARRAY_SIZE(p_out->simpleStruct); i++) {
+        g_free(p_out->simpleStruct[i].string);
+        g_free(p_out->simpleStruct[i].extdata);
+    }
+    g_free(p_out);
+}
+
+typedef struct SimpleStringList {
+    char *value;
+    struct SimpleStringList *next;
+} SimpleStringList;
+
+static void visit_type_SimpleStringList(Visitor *m, SimpleStringList ** obj,
+                                  const char *name, Error **errp)
+{
+    GenericList *i, **head = (GenericList **)obj;
+
+    visit_start_list(m, name, errp);
+
+    for (*head = i = visit_next_list(m, head, errp);
+         i;
+         i = visit_next_list(m, &i, errp)) {
+        SimpleStringList *native_i = (SimpleStringList *)i;
+        visit_type_str(m, &native_i->value, NULL, errp);
+    }
+
+    visit_end_list(m, errp);
+}
+
+static const uint8_t test_visitor_list_exp_cons[] = {
+    0x3f, 0x20, 0x80, 0x16, 0x05, 0x68, 0x65, 0x6c,
+    0x6c, 0x6f, 0x16, 0x05, 0x77, 0x6f, 0x72, 0x6c,
+    0x64, 0x00, 0x00,
+};
+
+static const uint8_t test_visitor_list_exp_prim[] = {
+    0x3f, 0x20, 0x0e, 0x16, 0x05, 0x68, 0x65, 0x6c,
+    0x6c, 0x6f, 0x16, 0x05, 0x77, 0x6f, 0x72, 0x6c,
+    0x64,
+};
+
+static void test_visitor_out_list(TestInputOutputVisitor *data,
+                                  const void *unused)
+{
+    SimpleStringList test_list = {
+        .value = (char *)"hello",
+        .next = &(SimpleStringList) {
+            .value = (char *)"world",
+            .next = NULL,
+        },
+    };
+    SimpleStringList *p_in = &test_list, *p_out = NULL, *iter = NULL, *_iter;
+    Error *errp = NULL;
+
+    visit_type_SimpleStringList(data->ov, &p_in, "SimpleStringList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_SimpleStringList(data->iv, &p_out, "SimpleStringList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    iter = p_out;
+    g_assert_cmpstr(iter->value, ==, "hello");
+    iter = iter->next;
+    g_assert_cmpstr(iter->value, ==, "world");
+
+    for (iter = p_out; iter; iter = _iter) {
+        _iter = iter->next;
+        g_free(iter->value);
+        g_free(iter);
+    }
+}
+
+typedef struct SimpleStructList {
+    SimpleStruct *value;
+    struct SimpleStructList *next;
+} SimpleStructList;
+
+static void visit_type_SimpleStructList(Visitor *m, SimpleStructList ** obj,
+                                        const char *name, Error **errp)
+{
+    GenericList *i, **head = (GenericList **)obj;
+
+    visit_start_list(m, name, errp);
+
+    for (*head = i = visit_next_list(m, head, errp);
+         i;
+         i = visit_next_list(m, &i, errp)) {
+        SimpleStructList *native_i = (SimpleStructList *)i;
+        visit_type_SimpleStruct(m, &native_i->value, NULL, errp);
+    }
+
+    visit_end_list(m, errp);
+}
+
+static const uint8_t test_visitor_simplestruct_list_exp_cons[] = {
+    0x3f, 0x20, 0x80, 0x30, 0x80, 0x42, 0x02, 0x03,
+    0xe8, 0x01, 0x01, 0x00, 0x16, 0x05, 0x68, 0x65,
+    0x6c, 0x6c, 0x6f, 0x04, 0x14, 0x00, 0x00, 0x11,
+    0x11, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x33,
+    0x33, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00,
+    0x03, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x30,
+    0x80, 0x42, 0x02, 0x07, 0xd0, 0x01, 0x01, 0x01,
+    0x16, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x04,
+    0x14, 0x11, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22,
+    0x22, 0x33, 0x33, 0x33, 0x33, 0x44, 0x44, 0x44,
+    0x44, 0x55, 0x55, 0x55, 0x55, 0x04, 0x0a, 0x00,
+    0x55, 0x00, 0x44, 0x00, 0x33, 0x00, 0x22, 0x00,
+    0x11, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t test_visitor_simplestruct_list_exp_prim[] = {
+    0x3f, 0x20, 0x64, 0x30, 0x30, 0x42, 0x02, 0x03,
+    0xe8, 0x01, 0x01, 0x00, 0x16, 0x05, 0x68, 0x65,
+    0x6c, 0x6c, 0x6f, 0x04, 0x14, 0x00, 0x00, 0x11,
+    0x11, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x33,
+    0x33, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0x55,
+    0x55, 0x04, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x00,
+    0x03, 0x00, 0x02, 0x00, 0x01, 0x30, 0x30, 0x42,
+    0x02, 0x07, 0xd0, 0x01, 0x01, 0x01, 0x16, 0x05,
+    0x77, 0x6f, 0x72, 0x6c, 0x64, 0x04, 0x14, 0x11,
+    0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x22, 0x33,
+    0x33, 0x33, 0x33, 0x44, 0x44, 0x44, 0x44, 0x55,
+    0x55, 0x55, 0x55, 0x04, 0x0a, 0x00, 0x55, 0x00,
+    0x44, 0x00, 0x33, 0x00, 0x22, 0x00, 0x11,
+};
+
+static void test_visitor_out_simplestruct_list(TestInputOutputVisitor *data,
+                                               const void *unused)
+{
+    uint16_t extdata1[] = { 5, 4, 3, 2, 1};
+    uint16_t extdata2[] = { 0x55, 0x44, 0x33, 0x22, 0x11};
+    SimpleStructList test_list = {
+        .value = &(SimpleStruct) {
+            .string = (char *)"hello",
+            .integer = 1000,
+            .boolean = false,
+            .data = {
+                0x1111, 0x2222, 0x3333, 0x4444, 0x5555
+            },
+            .extdata = extdata1,
+        },
+        .next = &(SimpleStructList) {
+            .value = &(SimpleStruct) {
+                .string = (char *)"world",
+                .integer = 2000,
+                .boolean = true,
+                .data = {
+                    0x11111111, 0x22222222, 0x33333333, 0x44444444,
+                    0x55555555
+                },
+                .extdata = extdata2,
+            },
+            .next = NULL,
+        },
+    };
+    SimpleStructList *p_in = &test_list, *p_out = NULL, *iter = NULL, *_iter;
+    Error *errp = NULL;
+    size_t i;
+
+    visit_type_SimpleStructList(data->ov, &p_in, "SimpleStructList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    visitor_input_setup(data);
+
+    visit_type_SimpleStructList(data->iv, &p_out, "SimpleStructList", &errp);
+    g_assert(!error_is_set(&errp));
+
+    iter = p_out;
+    g_assert_cmpstr(iter->value->string, ==, "hello");
+    for (i = 0; i < ARRAY_SIZE(test_list.value->data); i++) {
+        g_assert_cmpint(iter->value->data[i], ==, (i + 1) * 0x1111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata1); i++) {
+        g_assert_cmpint(iter->value->extdata[i], ==, (5 - i));
+    }
+
+    iter = iter->next;
+    g_assert_cmpstr(iter->value->string, ==, "world");
+    for (i = 0; i < ARRAY_SIZE(test_list.next->value->data); i++) {
+        g_assert_cmpint(iter->value->data[i], ==, (i + 1) * 0x11111111);
+    }
+    for (i = 0; i < ARRAY_SIZE(extdata2); i++) {
+        g_assert_cmpint(iter->value->extdata[i], ==, (5 - i) * 0x11);
+    }
+
+    for (iter = p_out; iter; iter = _iter) {
+        _iter = iter->next;
+        g_free(iter->value->string);
+        g_free(iter->value->extdata);
+        g_free(iter->value);
+        g_free(iter);
+    }
+}
+
+static void test_visitor_test_add(const char *testpath,
+                         BERTypePC ber_type,
+                         const TestExpResult *ter,
+                         void (*test_func)(TestInputOutputVisitor *data,
+                                           const void *user_data))
+{
+    char path[64];
+    void (*setup)(TestInputOutputVisitor *data, const void *unused);
+
+    switch (ber_type) {
+    case BER_TYPE_PRIMITIVE:
+        snprintf(path, sizeof(path), "/primitive%s", testpath);
+        setup = visitor_output_setup_primitive;
+        break;
+    default:
+        snprintf(path, sizeof(path), "/constructed%s", testpath);
+        setup = visitor_output_setup_constructed;
+        break;
+    }
+
+    g_test_add(path, TestInputOutputVisitor, ter, setup,
+               test_func, visitor_output_teardown);
+}
+
+struct test_data {
+    const char *path;
+    void (*test_func)(TestInputOutputVisitor *, const void *);
+    TestExpResult ter[2];
+};
+
+#define _TEST_CASE(FUNC, POST1, POST2, PATH) \
+    {\
+        .path = PATH,\
+        .test_func = test_visitor_out_## FUNC,\
+        .ter = {\
+            {\
+                .exp = test_visitor_## FUNC ## _exp ## POST1,\
+                .exp_len = sizeof(test_visitor_## FUNC ##_exp ## POST1),\
+            }, {\
+                .exp = test_visitor_## FUNC ##_exp ## POST2,\
+                .exp_len = sizeof(test_visitor_## FUNC ##_exp ## POST2),\
+            } \
+        } \
+    }
+
+#define TEST_CASE(PATH, FUNC) \
+    _TEST_CASE(FUNC, _cons, _prim, PATH)
+
+#define TEST_CASE_SIMPLE(PATH, FUNC) \
+    _TEST_CASE(FUNC, , , PATH)
+
+static const struct test_data tests[] = {
+    TEST_CASE_SIMPLE("/visitor/output/int", int),
+    TEST_CASE_SIMPLE("/visitor/output/bool", boolean),
+    TEST_CASE_SIMPLE("/visitor/output/string", string),
+    TEST_CASE_SIMPLE("/visitor/output/no-string", no_string),
+    TEST_CASE("/visitor/output/struct", struct),
+    TEST_CASE("/visitor/output/nested-struct", nested_struct),
+    TEST_CASE("/visitor/output/list", list),
+    TEST_CASE("/visitor/output/simplestruct_list", simplestruct_list),
+};
+
+int main(int argc, char **argv)
+{
+    BERTypePC types[] = {BER_TYPE_CONSTRUCTED, BER_TYPE_PRIMITIVE};
+    int i;
+    int j;
+
+    g_test_init(&argc, &argv, NULL);
+
+    for (i = 0; i < ARRAY_SIZE(types); i++) {
+        for (j = 0; j < ARRAY_SIZE(tests); j++) {
+            test_visitor_test_add(tests[j].path, types[i],
+                                  &tests[j].ter[i],
+                                  tests[j].test_func);
+        }
+    }
+
+    g_test_run();
+
+    return 0;
+}