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;
+}
