Patchwork [28/28] qidl: unit tests and build infrastructure

login
register
mail settings
Submitter Michael Roth
Date Oct. 31, 2012, 10:36 p.m.
Message ID <1351722972-17801-29-git-send-email-mdroth@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/196037/
State New
Headers show

Comments

Michael Roth - Oct. 31, 2012, 10:36 p.m.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 Makefile                     |    3 +
 configure                    |    1 +
 rules.mak                    |   42 ++++-
 tests/Makefile               |    8 +-
 tests/test-qidl-included.h   |   25 +++
 tests/test-qidl-linked.c     |  101 +++++++++++
 tests/test-qidl-pub-linked.c |   18 ++
 tests/test-qidl-pub-linked.h |   29 ++++
 tests/test-qidl.c            |  390 ++++++++++++++++++++++++++++++++++++++++++
 tests/test-qidl.h            |   36 ++++
 10 files changed, 650 insertions(+), 3 deletions(-)
 create mode 100644 tests/test-qidl-included.h
 create mode 100644 tests/test-qidl-linked.c
 create mode 100644 tests/test-qidl-pub-linked.c
 create mode 100644 tests/test-qidl-pub-linked.h
 create mode 100644 tests/test-qidl.c
 create mode 100644 tests/test-qidl.h

Patch

diff --git a/Makefile b/Makefile
index e543482..b575361 100644
--- a/Makefile
+++ b/Makefile
@@ -248,6 +248,8 @@  clean:
 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
 	rm -f $$d/qemu-options.def; \
         done
+	find -name '*.qidl.c' -exec rm -f {} \;
+	find -name '*.qidl.schema' -exec rm -f {} \;
 
 VERSION ?= $(shell cat VERSION)
 
@@ -419,6 +421,7 @@  ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
 Makefile: $(GENERATED_HEADERS)
 endif
 
+
 # Include automatically generated dependency files
 # Dependencies in Makefile.objs files come from our recursive subdir rules
 -include $(wildcard *.d tests/*.d)
diff --git a/configure b/configure
index 9c6ac87..bc675f8 100755
--- a/configure
+++ b/configure
@@ -3231,6 +3231,7 @@  if test "$debug_tcg" = "yes" ; then
 fi
 if test "$debug" = "yes" ; then
   echo "CONFIG_DEBUG_EXEC=y" >> $config_host_mak
+  echo "CONFIG_DEBUG_QIDL=y" >> $config_host_mak
 fi
 if test "$strip_opt" = "yes" ; then
   echo "STRIP=${strip}" >> $config_host_mak
diff --git a/rules.mak b/rules.mak
index 1b173aa..714f642 100644
--- a/rules.mak
+++ b/rules.mak
@@ -14,8 +14,23 @@  MAKEFLAGS += -rR
 # Flags for dependency generation
 QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d
 
+# Debug options for QIDL
+ifdef CONFIG_DEBUG_QIDL
+QIDL_FLAGS = --schema-filepath=$(*D)/$(*F).qidl.schema
+endif
+
+# QIDL dependencies
+qidl-deps := $(SRC_PATH)/qidl.h $(addprefix $(SRC_PATH)/scripts/,lexer.py qidl.py qidl_parser.py qapi.py qapi_visit.py)
+
+.SECONDARY:
+
 %.o: %.c
-	$(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<,"  CC    $(TARGET_DIR)$@")
+
+%.qidl.c: %.c $(qidl-deps)
+	$(call quiet-command,$(qidl-pp))
+
+%.o: %.c %.qidl.c
+	$(call quiet-command,$(qidl-cc))
 
 ifeq ($(LIBTOOL),)
 %.lo: %.c
@@ -79,6 +94,31 @@  TRACETOOL=$(PYTHON) $(SRC_PATH)/scripts/tracetool.py
 obj := .
 old-nested-dirs :=
 
+qidl-pp-cmd = \
+	$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(CFLAGS) -E -c -DQIDL_GEN $< | \
+	$(PYTHON) $(SRC_PATH)/scripts/qidl.py $(QIDL_FLAGS) \
+	--output-filepath=$(*D)/$(*F).qidl.c || [ "$$?" -eq 2 ]
+
+qidl-cc-cmd = \
+	$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c \
+	-DQIDL_ENABLED -include $< -o $@ $(*D)/$(*F).qidl.c
+
+default-cc-cmd = \
+	$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<
+
+qidl-pp = \
+	rm -f $(*D)/$(*F).qidl.*; \
+	if grep "QIDL_ENABLE()" $< 1>/dev/null; then \
+	  echo "qidl PP $(*D)/$(*F).c" && $(qidl-pp-cmd); \
+	fi
+
+qidl-cc = \
+	if test -f $(*D)/$(*F).qidl.c; then \
+	  echo "qidl CC $@" && $(qidl-cc-cmd); \
+	else \
+	  echo "  CC    $@" && $(default-cc-cmd); \
+	fi
+
 define push-var
 $(eval save-$2-$1 = $(value $1))
 $(eval $1 :=)
diff --git a/tests/Makefile b/tests/Makefile
index af06a9b..aab7ce2 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -15,6 +15,7 @@  check-unit-y += tests/test-string-output-visitor$(EXESUF)
 check-unit-y += tests/test-coroutine$(EXESUF)
 check-unit-y += tests/test-visitor-serialization$(EXESUF)
 check-unit-y += tests/test-iov$(EXESUF)
+check-unit-y += tests/test-qidl$(EXESUF)
 
 check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -34,11 +35,12 @@  test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.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-qmp-input-visitor.o tests/test-qmp-input-strict.o \
-	tests/test-qmp-commands.o tests/test-visitor-serialization.o
+	tests/test-qmp-commands.o tests/test-visitor-serialization.o \
+	tests/test-qidl.o
 
 test-qapi-obj-y =  $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y)
 test-qapi-obj-y += tests/test-qapi-visit.o tests/test-qapi-types.o
-test-qapi-obj-y += module.o
+test-qapi-obj-y += module.o $(qom-obj-y)
 
 $(test-obj-y): QEMU_INCLUDES += -Itests
 
@@ -84,6 +86,8 @@  check-qtest-$(CONFIG_POSIX)=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)
 qtest-obj-y = tests/libqtest.o $(oslib-obj-y) $(tools-obj-y)
 $(check-qtest-y): $(qtest-obj-y)
 
+tests/test-qidl$(EXESUF): tests/test-qidl.o tests/test-qidl-linked.o tests/test-qidl-pub-linked.o $(test-qapi-obj-y) qapi/misc-qapi-visit.o
+
 .PHONY: check-help
 check-help:
 	@echo "Regression testing targets:"
diff --git a/tests/test-qidl-included.h b/tests/test-qidl-included.h
new file mode 100644
index 0000000..3ee8487
--- /dev/null
+++ b/tests/test-qidl-included.h
@@ -0,0 +1,25 @@ 
+/*
+ * Unit-tests for QIDL-generated visitors/code
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth <mdroth@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.
+ */
+
+#ifndef TEST_QIDL_INCLUDED_H
+#define TEST_QIDL_INCLUDED_H
+
+#include "qidl.h"
+#include "test-qidl.h"
+
+typedef struct TestStructIncluded TestStructIncluded;
+
+QIDL_DECLARE(TestStructIncluded) {
+    TEST_QIDL_STRUCT_BODY
+};
+
+#endif
diff --git a/tests/test-qidl-linked.c b/tests/test-qidl-linked.c
new file mode 100644
index 0000000..0499bc1
--- /dev/null
+++ b/tests/test-qidl-linked.c
@@ -0,0 +1,101 @@ 
+/*
+ * Unit-tests for QIDL-generated visitors/code
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth <mdroth@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 "qidl.h"
+#include "test-qidl.h"
+#include "test-qidl-pub-linked.h"
+#include "hw/qdev-properties.h"
+#include "qapi/qmp-input-visitor.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qapi-dealloc-visitor.h"
+
+QIDL_ENABLE()
+
+typedef struct TestStructLinked TestStructLinked;
+
+QIDL_DECLARE(TestStructLinked) {
+    TEST_QIDL_STRUCT_BODY
+};
+
+/* exercise generated code from annotations in objects we link against */
+void test_linked_object_annotations(gconstpointer opaque)
+{
+    TestStructLinked *s1, *s2 = NULL;
+    QmpInputVisitor *qiv;
+    QmpOutputVisitor *qov;
+    QObject *s1_obj;
+    Error *err = NULL;
+
+    s1 = g_malloc0(sizeof(TestStructLinked));
+    fill_test_struct(s1);
+
+    qov = qmp_output_visitor_new();
+    QIDL_VISIT_TYPE(TestStructLinked, qmp_output_get_visitor(qov), &s1, NULL,
+                    &err);
+    g_assert(err == NULL);
+
+    s1_obj = qmp_output_get_qobject(qov);
+    qiv = qmp_input_visitor_new(s1_obj);
+
+    qobject_decref(s1_obj);
+    qmp_output_visitor_cleanup(qov);
+    free_test_struct(s1);
+
+    s2 = g_malloc0(sizeof(TestStructLinked));
+    QIDL_VISIT_TYPE(TestStructLinked, qmp_input_get_visitor(qiv), &s2, NULL,
+                    &err);
+    g_assert(err == NULL);
+    check_test_struct(s2);
+
+    qmp_input_visitor_cleanup(qiv);
+    free_test_struct(s2);
+
+    check_test_struct_properties(QIDL_PROPERTIES(TestStructLinked));
+}
+
+/* exercise annotations in public header files who's generated code we
+ * link against (second user of generated code)
+ */
+void test_public_linked_object_annotations2(gconstpointer opaque)
+{
+    TestStructPublicLinked *s1, *s2 = NULL;
+    QmpInputVisitor *qiv;
+    QmpOutputVisitor *qov;
+    QObject *s1_obj;
+    Error *err = NULL;
+
+    s1 = g_malloc0(sizeof(TestStructPublicLinked));
+    fill_test_struct(s1);
+
+    qov = qmp_output_visitor_new();
+    QIDL_VISIT_TYPE(TestStructPublicLinked, qmp_output_get_visitor(qov), &s1,
+                    NULL, &err);
+    g_assert(err == NULL);
+
+    s1_obj = qmp_output_get_qobject(qov);
+    qiv = qmp_input_visitor_new(s1_obj);
+
+    qobject_decref(s1_obj);
+    qmp_output_visitor_cleanup(qov);
+    free_test_struct(s1);
+
+    s2 = g_malloc0(sizeof(TestStructPublicLinked));
+    QIDL_VISIT_TYPE(TestStructPublicLinked, qmp_input_get_visitor(qiv), &s2,
+                    NULL, &err);
+    g_assert(err == NULL);
+    check_test_struct(s2);
+
+    qmp_input_visitor_cleanup(qiv);
+    free_test_struct(s2);
+
+    check_test_struct_properties(QIDL_PROPERTIES(TestStructPublicLinked));
+}
diff --git a/tests/test-qidl-pub-linked.c b/tests/test-qidl-pub-linked.c
new file mode 100644
index 0000000..5b613e7
--- /dev/null
+++ b/tests/test-qidl-pub-linked.c
@@ -0,0 +1,18 @@ 
+/*
+ * Unit-tests for QIDL-generated visitors/code
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth <mdroth@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 "qidl.h"
+#include "test-qidl-pub-linked.h"
+
+QIDL_ENABLE()
+
+QIDL_IMPLEMENT_PUBLIC(TestStructPublicLinked)
diff --git a/tests/test-qidl-pub-linked.h b/tests/test-qidl-pub-linked.h
new file mode 100644
index 0000000..362b57c
--- /dev/null
+++ b/tests/test-qidl-pub-linked.h
@@ -0,0 +1,29 @@ 
+/*
+ * Unit-tests for QIDL-generated visitors/code
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth <mdroth@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.
+ */
+
+#ifndef TEST_QIDL_PUB_LINKED_H
+#define TEST_QIDL_PUB_LINKED_H
+
+typedef struct TestStructPublicLinked TestStructPublicLinked;
+
+QIDL_DECLARE_PUBLIC(TestStructPublicLinked) {
+    int32_t q_immutable a;
+    int32_t b;
+    uint32_t q_immutable c;
+    uint32_t d;
+    uint64_t q_immutable e;
+    uint64_t q_property("f", 42) f;
+    char *g;
+    char q_property("h") *h;
+};
+
+#endif
diff --git a/tests/test-qidl.c b/tests/test-qidl.c
new file mode 100644
index 0000000..06cf27e
--- /dev/null
+++ b/tests/test-qidl.c
@@ -0,0 +1,390 @@ 
+/*
+ * Unit-tests for QIDL-generated visitors/code
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth <mdroth@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 <stdlib.h>
+#include <stdint.h>
+#include "qidl.h"
+#include "test-qidl.h"
+#include "test-qidl-included.h"
+#include "test-qidl-pub-linked.h"
+#include "hw/qdev-properties.h"
+#include "qapi/qmp-input-visitor.h"
+#include "qapi/qmp-output-visitor.h"
+#include "qapi/qapi-dealloc-visitor.h"
+
+QIDL_ENABLE()
+
+PropertyInfo qdev_prop_uint64;
+PropertyInfo qdev_prop_string;
+
+typedef struct TestStructMain TestStructMain;
+
+QIDL_DECLARE(TestStructMain) {
+    TEST_QIDL_STRUCT_BODY
+};
+
+void fill_test_struct(void *opaque)
+{
+    TestStructMain *s = opaque;
+    s->a = 42;
+    s->b = INT32_MAX;
+    s->c = 43;
+    s->d = UINT32_MAX;
+    s->e = 44;
+    s->f = UINT64_MAX;
+    s->g = g_strdup("test string g");
+    s->h = g_strdup("test string h");
+}
+
+void check_test_struct(void *opaque)
+{
+    TestStructMain *s = opaque;
+    g_assert_cmpint(s->a, ==, 0);
+    g_assert_cmpint(s->b, ==, INT32_MAX);
+    g_assert_cmpint(s->c, ==, 0);
+    g_assert_cmpint(s->d, ==, UINT32_MAX);
+    g_assert_cmpint(s->e, ==, 0);
+    g_assert_cmpstr(s->g, ==, "test string g");
+    g_assert(s->h == NULL);
+}
+
+void free_test_struct(void *opaque)
+{
+    TestStructMain *s = opaque;
+    g_free(s->g);
+    g_free(s->h);
+    g_free(s);
+}
+
+void check_test_struct_properties(const Property *props)
+{
+    g_assert_cmpstr(props[0].name, ==, "f");
+    g_assert_cmpint(props[0].defval, ==, 42);
+    g_assert_cmpstr(props[1].name, ==, "h");
+    g_assert_cmpint(props[1].defval, ==, 0);
+    g_assert(props[2].name == NULL);
+}
+
+/* exercise generated code from annotations in main() object file */
+static void test_main_object_annotations(gconstpointer opaque)
+{
+    TestStructMain *s1, *s2 = NULL;
+    QmpInputVisitor *qiv;
+    QmpOutputVisitor *qov;
+    QObject *s1_obj;
+    Error *err = NULL;
+
+    s1 = g_malloc0(sizeof(TestStructMain));
+    fill_test_struct(s1);
+
+    qov = qmp_output_visitor_new();
+    QIDL_VISIT_TYPE(TestStructMain, qmp_output_get_visitor(qov), &s1, NULL,
+                    &err);
+    g_assert(err == NULL);
+
+    s1_obj = qmp_output_get_qobject(qov);
+    qiv = qmp_input_visitor_new(s1_obj);
+
+    qobject_decref(s1_obj);
+    qmp_output_visitor_cleanup(qov);
+    free_test_struct(s1);
+
+    s2 = g_malloc0(sizeof(TestStructMain));
+    QIDL_VISIT_TYPE(TestStructMain, qmp_input_get_visitor(qiv), &s2, NULL,
+                    &err);
+    g_assert(err == NULL);
+    check_test_struct(s2);
+
+    qmp_input_visitor_cleanup(qiv);
+    free_test_struct(s2);
+
+    check_test_struct_properties(QIDL_PROPERTIES(TestStructMain));
+}
+
+/* exercise generated code from annotations in included header files */
+static void test_header_file_annotations(gconstpointer opaque)
+{
+    TestStructIncluded *s1, *s2 = NULL;
+    QmpInputVisitor *qiv;
+    QmpOutputVisitor *qov;
+    QObject *s1_obj;
+    Error *err = NULL;
+
+    s1 = g_malloc0(sizeof(TestStructIncluded));
+    fill_test_struct(s1);
+
+    qov = qmp_output_visitor_new();
+    QIDL_VISIT_TYPE(TestStructIncluded, qmp_output_get_visitor(qov), &s1, NULL,
+                    &err);
+    g_assert(err == NULL);
+
+    s1_obj = qmp_output_get_qobject(qov);
+    qiv = qmp_input_visitor_new(s1_obj);
+
+    qobject_decref(s1_obj);
+    qmp_output_visitor_cleanup(qov);
+    free_test_struct(s1);
+
+    s2 = g_malloc0(sizeof(TestStructIncluded));
+    QIDL_VISIT_TYPE(TestStructIncluded, qmp_input_get_visitor(qiv), &s2, NULL,
+                    &err);
+    g_assert(err == NULL);
+    check_test_struct(s2);
+
+    qmp_input_visitor_cleanup(qiv);
+    free_test_struct(s2);
+
+    check_test_struct_properties(QIDL_PROPERTIES(TestStructIncluded));
+}
+
+/* exercise annotations in public header files who's generated code we
+ * link against
+ */
+static void test_public_linked_object_annotations(gconstpointer opaque)
+{
+    TestStructPublicLinked *s1, *s2 = NULL;
+    QmpInputVisitor *qiv;
+    QmpOutputVisitor *qov;
+    QObject *s1_obj;
+    Error *err = NULL;
+
+    s1 = g_malloc0(sizeof(TestStructPublicLinked));
+    fill_test_struct(s1);
+
+    qov = qmp_output_visitor_new();
+    QIDL_VISIT_TYPE(TestStructPublicLinked, qmp_output_get_visitor(qov), &s1,
+                    NULL, &err);
+    g_assert(err == NULL);
+
+    s1_obj = qmp_output_get_qobject(qov);
+    qiv = qmp_input_visitor_new(s1_obj);
+
+    qobject_decref(s1_obj);
+    qmp_output_visitor_cleanup(qov);
+    free_test_struct(s1);
+
+    s2 = g_malloc0(sizeof(TestStructPublicLinked));
+    QIDL_VISIT_TYPE(TestStructPublicLinked, qmp_input_get_visitor(qiv), &s2,
+                    NULL, &err);
+    g_assert(err == NULL);
+    check_test_struct(s2);
+
+    qmp_input_visitor_cleanup(qiv);
+    free_test_struct(s2);
+
+    check_test_struct_properties(QIDL_PROPERTIES(TestStructPublicLinked));
+}
+
+typedef struct TestStructComplex TestStructComplex;
+
+QIDL_DECLARE(TestStructComplex) {
+    int8_t q_size(2) array1[4];
+    size_t array2_count;
+    int32_t q_size(array2_count) array2[8];
+    int16_t q_size((2*3)) array3[16];
+    bool has_optional_array;
+    int32_t optional_array_count;
+    int8_t q_optional q_size(optional_array_count) optional_array[16];
+    TestStructMain struct_array[32];
+    int64_t struct_array2_count;
+    TestStructMain q_size(struct_array2_count) struct_array2[32];
+};
+
+static void fill_test_struct_complex(TestStructComplex *s)
+{
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        s->array1[i] = i*2;
+    }
+
+    s->array2_count = 6;
+    for (i = 0; i < s->array2_count; i++) {
+        s->array2[i] = i*2;
+    }
+
+    for (i = 0; i < 6; i++) {
+        s->array3[i] = i*2;
+    }
+
+    s->has_optional_array = true;
+    s->optional_array_count = 15;
+    for (i = 0; i < s->optional_array_count; i++) {
+        s->optional_array[i] = i*2;
+    }
+
+    for (i = 0; i < 32; i++) {
+        fill_test_struct(&s->struct_array[i]);
+    }
+
+    s->struct_array2_count = 31;
+    for (i = 0; i < s->struct_array2_count; i++) {
+        fill_test_struct(&s->struct_array2[i]);
+    }
+}
+
+static void check_test_struct_complex(TestStructComplex *s)
+{
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        if (i < 2) {
+            g_assert_cmpint(s->array1[i], ==, i*2);
+        } else {
+            g_assert_cmpint(s->array1[i], ==, 0);
+        }
+    }
+
+    g_assert_cmpint(s->array2_count, ==, 6);
+    for (i = 0; i < sizeof(s->array2)/sizeof(int32_t); i++) {
+        if (i < s->array2_count) {
+            g_assert_cmpint(s->array2[i], ==, i*2);
+        } else {
+            g_assert_cmpint(s->array2[i], ==, 0);
+        }
+    }
+
+    g_assert(s->has_optional_array);
+    g_assert_cmpint(s->optional_array_count, ==, 15);
+    for (i = 0; i < sizeof(s->optional_array)/sizeof(int16_t); i++) {
+        if (i < s->optional_array_count) {
+            g_assert_cmpint(s->optional_array[i], ==, i*2);
+        } else {
+            g_assert_cmpint(s->optional_array[i], ==, 0);
+        }
+    }
+
+    for (i = 0; i < 32; i++) {
+        check_test_struct(&s->struct_array[i]);
+    }
+
+    g_assert_cmpint(s->struct_array2_count, ==, 31);
+    for (i = 0; i < sizeof(s->struct_array2)/sizeof(TestStructMain); i++) {
+        if (i < s->struct_array2_count) {
+            check_test_struct(&s->struct_array2[i]);
+        } else {
+            int j;
+            uint8_t *ptr = (uint8_t *)&s->struct_array2[i];
+            for (j = 0; j < sizeof(TestStructMain); j++) {
+                g_assert_cmpint(ptr[0], ==, 0);
+            }
+        }
+    }
+}
+
+static void test_array_annotations(gconstpointer opaque)
+{
+    TestStructComplex *s1, *s2 = NULL;
+    QmpInputVisitor *qiv;
+    QmpOutputVisitor *qov;
+    QObject *s1_obj;
+    Error *err = NULL;
+
+    s1 = g_malloc0(sizeof(TestStructComplex));
+    fill_test_struct_complex(s1);
+
+    qov = qmp_output_visitor_new();
+    QIDL_VISIT_TYPE(TestStructComplex, qmp_output_get_visitor(qov), &s1, NULL,
+                    &err);
+    g_assert(err == NULL);
+
+    s1_obj = qmp_output_get_qobject(qov);
+    qiv = qmp_input_visitor_new(s1_obj);
+
+    qobject_decref(s1_obj);
+    qmp_output_visitor_cleanup(qov);
+    g_free(s1);
+
+    s2 = g_malloc0(sizeof(TestStructComplex));
+    QIDL_VISIT_TYPE(TestStructComplex, qmp_input_get_visitor(qiv), &s2, NULL,
+                    &err);
+    g_assert(err == NULL);
+    check_test_struct_complex(s2);
+
+    qmp_input_visitor_cleanup(qiv);
+    g_free(s2);
+}
+
+typedef struct TestStructComplex2 TestStructComplex2;
+
+QIDL_DECLARE(TestStructComplex2) {
+    bool has_struct1;
+    TestStructMain q_optional *struct1;
+    TestStructMain embedded_struct1;
+};
+
+static void test_complex_annotations(gconstpointer opaque)
+{
+    TestStructComplex2 *s1, *s2 = NULL;
+    QmpInputVisitor *qiv;
+    QmpOutputVisitor *qov;
+    QObject *s1_obj;
+    Error *err = NULL;
+
+    s1 = g_malloc0(sizeof(TestStructComplex));
+    s1->has_struct1 = true;
+    s1->struct1 = g_malloc0(sizeof(TestStructMain));
+    fill_test_struct(s1->struct1);
+    fill_test_struct(&s1->embedded_struct1);
+
+    qov = qmp_output_visitor_new();
+    QIDL_VISIT_TYPE(TestStructComplex2, qmp_output_get_visitor(qov), &s1, NULL,
+                    &err);
+    g_assert(err == NULL);
+
+    s1_obj = qmp_output_get_qobject(qov);
+    qiv = qmp_input_visitor_new(s1_obj);
+
+    qobject_decref(s1_obj);
+    qmp_output_visitor_cleanup(qov);
+    free_test_struct(s1->struct1);
+    g_free(s1);
+
+    s2 = g_malloc0(sizeof(TestStructComplex2));
+    QIDL_VISIT_TYPE(TestStructComplex2, qmp_input_get_visitor(qiv), &s2, NULL,
+                    &err);
+    g_assert(err == NULL);
+    check_test_struct(s2->struct1);
+    check_test_struct(&s2->embedded_struct1);
+
+    qmp_input_visitor_cleanup(qiv);
+    free_test_struct(s2->struct1);
+    g_free(s2);
+}
+
+int main(int argc, char **argv)
+{
+    module_call_init(MODULE_INIT_QOM);
+    module_call_init(MODULE_INIT_QIDL);
+
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_data_func("/qidl/build_test/main_object_annotations", NULL,
+                         test_main_object_annotations);
+    g_test_add_data_func("/qidl/build_test/linked_object_annotations", NULL,
+                         test_linked_object_annotations);
+    g_test_add_data_func("/qidl/build_test/public_linked_object_annotations",
+                         NULL, test_public_linked_object_annotations);
+    g_test_add_data_func("/qidl/build_test/public_linked_object_annotations2",
+                         NULL, test_public_linked_object_annotations2);
+    g_test_add_data_func("/qidl/build_test/header_file_annotations", NULL,
+                         test_header_file_annotations);
+    g_test_add_data_func("/qidl/build_test/array_annotations", NULL,
+                         test_array_annotations);
+    g_test_add_data_func("/qidl/build_test/complex_annotations", NULL,
+                         test_complex_annotations);
+
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-qidl.h b/tests/test-qidl.h
new file mode 100644
index 0000000..ddb03ad
--- /dev/null
+++ b/tests/test-qidl.h
@@ -0,0 +1,36 @@ 
+/*
+ * Unit-tests for QIDL-generated visitors/code
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth <mdroth@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.
+ */
+
+#ifndef TEST_QIDL_H
+#define TEST_QIDL_H
+
+#include "qidl.h"
+
+#define TEST_QIDL_STRUCT_BODY               \
+    int32_t q_immutable a;                  \
+    int32_t b;                              \
+    uint32_t q_immutable c;                 \
+    uint32_t d;                             \
+    uint64_t q_immutable e;                 \
+    uint64_t q_property("f", 42) f;         \
+    char *g;                                \
+    char q_property("h") *h;
+
+void fill_test_struct(void *opaque);
+void check_test_struct(void *opaque);
+void free_test_struct(void *opaque);
+void check_test_struct_properties(const Property *props);
+
+void test_linked_object_annotations(gconstpointer opaque);
+void test_public_linked_object_annotations2(gconstpointer opaque);
+
+#endif