summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile167
-rw-r--r--tests/check-qdict.c378
-rw-r--r--tests/check-qfloat.c53
-rw-r--r--tests/check-qint.c87
-rw-r--r--tests/check-qjson.c728
-rw-r--r--tests/check-qlist.c127
-rw-r--r--tests/check-qstring.c107
-rw-r--r--tests/libqtest.c387
-rw-r--r--tests/libqtest.h335
-rw-r--r--tests/rtc-test.c263
-rw-r--r--tests/test-coroutine.c219
-rw-r--r--tests/test-qmp-commands.c139
-rw-r--r--tests/test-qmp-input-strict.c234
-rw-r--r--tests/test-qmp-input-visitor.c308
-rw-r--r--tests/test-qmp-output-visitor.c477
-rw-r--r--tests/test-string-input-visitor.c195
-rw-r--r--tests/test-string-output-visitor.c188
17 files changed, 4350 insertions, 42 deletions
diff --git a/tests/Makefile b/tests/Makefile
index 94ea3421ad..a98a848ec9 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,58 +1,141 @@
 export SRC_PATH
 
-CHECKS = check-qdict check-qfloat check-qint check-qstring check-qlist
-CHECKS += check-qjson test-qmp-output-visitor test-qmp-input-visitor
-CHECKS += test-string-input-visitor test-string-output-visitor test-coroutine
-CHECKS += test-qmp-commands
-CHECKS += $(SRC_PATH)/tests/qemu-iotests-quick.sh
-
-check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS)
-
-check-qint: check-qint.o qint.o $(tools-obj-y)
-check-qstring: check-qstring.o qstring.o $(tools-obj-y)
-check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
-check-qlist: check-qlist.o qlist.o qint.o $(tools-obj-y)
-check-qfloat: check-qfloat.o qfloat.o $(tools-obj-y)
-check-qjson: check-qjson.o $(qobject-obj-y) $(tools-obj-y)
-test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(tools-obj-y)
-
-test-qmp-input-visitor.o test-qmp-output-visitor.o \
-test-string-input-visitor.o test-string-output-visitor.o \
-	test-qmp-commands.o: QEMU_CFLAGS += -I $(qapi-dir)
-
-$(qapi-dir)/test-qapi-types.c $(qapi-dir)/test-qapi-types.h :\
+check-unit-y = tests/check-qdict$(EXESUF)
+check-unit-y += tests/check-qfloat$(EXESUF)
+check-unit-y += tests/check-qint$(EXESUF)
+check-unit-y += tests/check-qstring$(EXESUF)
+check-unit-y += tests/check-qlist$(EXESUF)
+check-unit-y += tests/check-qjson$(EXESUF)
+check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
+check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
+check-unit-y += tests/test-qmp-input-strict$(EXESUF)
+check-unit-y += tests/test-qmp-commands$(EXESUF)
+check-unit-y += tests/test-string-input-visitor$(EXESUF)
+check-unit-y += tests/test-string-output-visitor$(EXESUF)
+check-unit-y += tests/test-coroutine$(EXESUF)
+
+check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
+
+# All QTests for now are POSIX-only, but the dependencies are
+# really in libqtest, not in the testcases themselves.
+check-qtest-i386-y = tests/rtc-test
+check-qtest-x86_64-y = $(check-qtest-i386-y)
+
+GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
+
+test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
+	tests/check-qlist.o tests/check-qfloat.o tests/check-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-qmp-input-visitor.o tests/test-qmp-input-strict.o \
+	tests/test-qmp-commands.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-obj-y): $(GENERATED_HEADERS)
+$(test-obj-y): QEMU_INCLUDES += -Itests
+
+tests/check-qint$(EXESUF): tests/check-qint.o qint.o $(tools-obj-y)
+tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o $(tools-obj-y)
+tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
+tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o $(tools-obj-y)
+tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o $(tools-obj-y)
+tests/check-qjson$(EXESUF): tests/check-qjson.o $(qobject-obj-y) $(tools-obj-y)
+tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(coroutine-obj-y) $(tools-obj-y)
+
+tests/test-qapi-types.c tests/test-qapi-types.h :\
 $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "$(qapi-dir)" -p "test-" < $<, "  GEN   $@")
-$(qapi-dir)/test-qapi-visit.c $(qapi-dir)/test-qapi-visit.h :\
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
+tests/test-qapi-visit.c tests/test-qapi-visit.h :\
 $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "$(qapi-dir)" -p "test-" < $<, "  GEN   $@")
-$(qapi-dir)/test-qmp-commands.h $(qapi-dir)/test-qmp-marshal.c :\
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
+tests/test-qmp-commands.h tests/test-qmp-marshal.c :\
 $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
-	    $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o "$(qapi-dir)" -p "test-" < $<, "  GEN   $@")
+	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o tests -p "test-" < $<, "  GEN   $@")
 
 
-test-string-output-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-string-output-visitor: test-string-output-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+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-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y)
+tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y)
+tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
 
-test-string-input-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-string-input-visitor: test-string-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y)
 
-test-qmp-output-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-qmp-output-visitor: test-qmp-output-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+# QTest rules
 
-test-qmp-input-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-qmp-input-visitor: test-qmp-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
+QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),))
+check-qtest-$(CONFIG_POSIX)=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
 
-test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
-test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
+qtest-obj-y = tests/libqtest.o $(oslib-obj-y)
+$(check-qtest-y): $(qtest-obj-y)
 
-$(SRC_PATH)/tests/qemu-iotests-quick.sh: qemu-img qemu-io
+.PHONY: check-help
+check-help:
+	@echo "Regression testing targets:"
+	@echo
+	@echo " make check                Run all tests"
+	@echo " make check-qtest-TARGET   Run qtest tests for given target"
+	@echo " make check-qtest          Run qtest tests"
+	@echo " make check-unit           Run qobject tests"
+	@echo " make check-block          Run block tests"
+	@echo " make check-report.html    Generates an HTML test report"
+	@echo
+	@echo "Please note that HTML reports do not regenerate if the unit tests"
+	@echo "has not changed."
+	@echo
+	@echo "The variable SPEED can be set to control the gtester speed setting."
+	@echo "Default options are -k and (for make V=1) --verbose; they can be"
+	@echo "changed with variable GTESTER_OPTIONS."
 
+.SECONDARY:
 
-.PHONY: check check-block
+SPEED = quick
+GTESTER_OPTIONS = -k $(if $(V),--verbose,-q)
 
-check: $(CHECKS)
-	$(call quiet-command, gtester $(CHECKS), "  CHECK")
+# gtester tests, possibly with verbose output
 
-check-block:
-	$(call quiet-command, $(SHELL) $(SRC_PATH)/tests/check-block.sh , "  CHECK")
+.PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS))
+$(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y)
+	$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
+		gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
+
+.PHONY: $(patsubst %, check-%, $(check-unit-y))
+$(patsubst %, check-%, $(check-unit-y)): check-%: %
+	$(call quiet-command,gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER $*")
+
+# gtester tests with XML output
+
+$(patsubst %, check-report-qtest-%.xml, $(QTEST_TARGETS)): check-report-qtest-%.xml: $(check-qtest-y)
+	$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
+	  gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
+
+check-report-unit.xml: $(check-unit-y)
+	$(call quiet-command,gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $^, "GTESTER $@")
+
+# Reports and overall runs
+
+check-report.xml: $(patsubst %,check-report-qtest-%.xml, $(QTEST_TARGETS)) check-report-unit.xml
+	$(call quiet-command,$(SRC_PATH)/scripts/gtester-cat $^ > $@, "  GEN    $@")
+
+check-report.html: check-report.xml
+	$(call quiet-command,gtester-report $< > $@, "  GEN    $@")
+
+
+# Other tests
+
+.PHONY: check-tests/qemu-iotests-quick.sh
+check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF)
+	$<
+
+# Consolidated targets
+
+.PHONY: check-qtest check-unit check
+check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
+check-unit: $(patsubst %,check-%, $(check-unit-y))
+check-block: $(patsubst %,check-%, $(check-block-y))
+check: check-unit check-qtest
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
new file mode 100644
index 0000000000..fc0d276538
--- /dev/null
+++ b/tests/check-qdict.c
@@ -0,0 +1,378 @@
+/*
+ * QDict unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 <glib.h>
+
+#include "qint.h"
+#include "qdict.h"
+#include "qstring.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+static void qdict_new_test(void)
+{
+    QDict *qdict;
+
+    qdict = qdict_new();
+    g_assert(qdict != NULL);
+    g_assert(qdict_size(qdict) == 0);
+    g_assert(qdict->base.refcnt == 1);
+    g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT);
+
+    // destroy doesn't exit yet
+    g_free(qdict);
+}
+
+static void qdict_put_obj_test(void)
+{
+    QInt *qi;
+    QDict *qdict;
+    QDictEntry *ent;
+    const int num = 42;
+
+    qdict = qdict_new();
+
+    // key "" will have tdb hash 12345
+    qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num)));
+
+    g_assert(qdict_size(qdict) == 1);
+    ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]);
+    qi = qobject_to_qint(ent->value);
+    g_assert(qint_get_int(qi) == num);
+
+    // destroy doesn't exit yet
+    QDECREF(qi);
+    g_free(ent->key);
+    g_free(ent);
+    g_free(qdict);
+}
+
+static void qdict_destroy_simple_test(void)
+{
+    QDict *qdict;
+
+    qdict = qdict_new();
+    qdict_put_obj(qdict, "num", QOBJECT(qint_from_int(0)));
+    qdict_put_obj(qdict, "str", QOBJECT(qstring_from_str("foo")));
+
+    QDECREF(qdict);
+}
+
+static void qdict_get_test(void)
+{
+    QInt *qi;
+    QObject *obj;
+    const int value = -42;
+    const char *key = "test";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qint_from_int(value));
+
+    obj = qdict_get(tests_dict, key);
+    g_assert(obj != NULL);
+
+    qi = qobject_to_qint(obj);
+    g_assert(qint_get_int(qi) == value);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_get_int_test(void)
+{
+    int ret;
+    const int value = 100;
+    const char *key = "int";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qint_from_int(value));
+
+    ret = qdict_get_int(tests_dict, key);
+    g_assert(ret == value);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_get_try_int_test(void)
+{
+    int ret;
+    const int value = 100;
+    const char *key = "int";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qint_from_int(value));
+
+    ret = qdict_get_try_int(tests_dict, key, 0);
+    g_assert(ret == value);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_get_str_test(void)
+{
+    const char *p;
+    const char *key = "key";
+    const char *str = "string";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qstring_from_str(str));
+
+    p = qdict_get_str(tests_dict, key);
+    g_assert(p != NULL);
+    g_assert(strcmp(p, str) == 0);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_get_try_str_test(void)
+{
+    const char *p;
+    const char *key = "key";
+    const char *str = "string";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qstring_from_str(str));
+
+    p = qdict_get_try_str(tests_dict, key);
+    g_assert(p != NULL);
+    g_assert(strcmp(p, str) == 0);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_haskey_not_test(void)
+{
+    QDict *tests_dict = qdict_new();
+    g_assert(qdict_haskey(tests_dict, "test") == 0);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_haskey_test(void)
+{
+    const char *key = "test";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qint_from_int(0));
+    g_assert(qdict_haskey(tests_dict, key) == 1);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_del_test(void)
+{
+    const char *key = "key test";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qstring_from_str("foo"));
+    g_assert(qdict_size(tests_dict) == 1);
+
+    qdict_del(tests_dict, key);
+
+    g_assert(qdict_size(tests_dict) == 0);
+    g_assert(qdict_haskey(tests_dict, key) == 0);
+
+    QDECREF(tests_dict);
+}
+
+static void qobject_to_qdict_test(void)
+{
+    QDict *tests_dict = qdict_new();
+    g_assert(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_iterapi_test(void)
+{
+    int count;
+    const QDictEntry *ent;
+    QDict *tests_dict = qdict_new();
+
+    g_assert(qdict_first(tests_dict) == NULL);
+
+    qdict_put(tests_dict, "key1", qint_from_int(1));
+    qdict_put(tests_dict, "key2", qint_from_int(2));
+    qdict_put(tests_dict, "key3", qint_from_int(3));
+
+    count = 0;
+    for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
+        g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
+        count++;
+    }
+
+    g_assert(count == qdict_size(tests_dict));
+
+    /* Do it again to test restarting */
+    count = 0;
+    for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
+        g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
+        count++;
+    }
+
+    g_assert(count == qdict_size(tests_dict));
+
+    QDECREF(tests_dict);
+}
+
+/*
+ * Errors test-cases
+ */
+
+static void qdict_put_exists_test(void)
+{
+    int value;
+    const char *key = "exists";
+    QDict *tests_dict = qdict_new();
+
+    qdict_put(tests_dict, key, qint_from_int(1));
+    qdict_put(tests_dict, key, qint_from_int(2));
+
+    value = qdict_get_int(tests_dict, key);
+    g_assert(value == 2);
+
+    g_assert(qdict_size(tests_dict) == 1);
+
+    QDECREF(tests_dict);
+}
+
+static void qdict_get_not_exists_test(void)
+{
+    QDict *tests_dict = qdict_new();
+    g_assert(qdict_get(tests_dict, "foo") == NULL);
+
+    QDECREF(tests_dict);
+}
+
+/*
+ * Stress test-case
+ *
+ * This is a lot big for a unit-test, but there is no other place
+ * to have it.
+ */
+
+static void remove_dots(char *string)
+{
+    char *p = strchr(string, ':');
+    if (p)
+        *p = '\0';
+}
+
+static QString *read_line(FILE *file, char *key)
+{
+    char value[128];
+
+    if (fscanf(file, "%127s%127s", key, value) == EOF) {
+        return NULL;
+    }
+    remove_dots(key);
+    return qstring_from_str(value);
+}
+
+#define reset_file(file)    fseek(file, 0L, SEEK_SET)
+
+static void qdict_stress_test(void)
+{
+    size_t lines;
+    char key[128];
+    FILE *test_file;
+    QDict *qdict;
+    QString *value;
+    const char *test_file_path = "qdict-test-data.txt";
+
+    test_file = fopen(test_file_path, "r");
+    g_assert(test_file != NULL);
+
+    // Create the dict
+    qdict = qdict_new();
+    g_assert(qdict != NULL);
+
+    // Add everything from the test file
+    for (lines = 0;; lines++) {
+        value = read_line(test_file, key);
+        if (!value)
+            break;
+
+        qdict_put(qdict, key, value);
+    }
+    g_assert(qdict_size(qdict) == lines);
+
+    // Check if everything is really in there
+    reset_file(test_file);
+    for (;;) {
+        const char *str1, *str2;
+
+        value = read_line(test_file, key);
+        if (!value)
+            break;
+
+        str1 = qstring_get_str(value);
+
+        str2 = qdict_get_str(qdict, key);
+        g_assert(str2 != NULL);
+
+        g_assert(strcmp(str1, str2) == 0);
+
+        QDECREF(value);
+    }
+
+    // Delete everything
+    reset_file(test_file);
+    for (;;) {
+        value = read_line(test_file, key);
+        if (!value)
+            break;
+
+        qdict_del(qdict, key);
+        QDECREF(value);
+
+        g_assert(qdict_haskey(qdict, key) == 0);
+    }
+    fclose(test_file);
+
+    g_assert(qdict_size(qdict) == 0);
+    QDECREF(qdict);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/public/new", qdict_new_test);
+    g_test_add_func("/public/put_obj", qdict_put_obj_test);
+    g_test_add_func("/public/destroy_simple", qdict_destroy_simple_test);
+
+    /* Continue, but now with fixtures */
+    g_test_add_func("/public/get", qdict_get_test);
+    g_test_add_func("/public/get_int", qdict_get_int_test);
+    g_test_add_func("/public/get_try_int", qdict_get_try_int_test);
+    g_test_add_func("/public/get_str", qdict_get_str_test);
+    g_test_add_func("/public/get_try_str", qdict_get_try_str_test);
+    g_test_add_func("/public/haskey_not", qdict_haskey_not_test);
+    g_test_add_func("/public/haskey", qdict_haskey_test);
+    g_test_add_func("/public/del", qdict_del_test);
+    g_test_add_func("/public/to_qdict", qobject_to_qdict_test);
+    g_test_add_func("/public/iterapi", qdict_iterapi_test);
+
+    g_test_add_func("/errors/put_exists", qdict_put_exists_test);
+    g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
+
+    /* The Big one */
+    if (g_test_slow()) {
+        g_test_add_func("/stress/test", qdict_stress_test);
+    }
+
+    return g_test_run();
+}
diff --git a/tests/check-qfloat.c b/tests/check-qfloat.c
new file mode 100644
index 0000000000..cdc66ea10b
--- /dev/null
+++ b/tests/check-qfloat.c
@@ -0,0 +1,53 @@
+/*
+ * QFloat unit-tests.
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * 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 <glib.h>
+
+#include "qfloat.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+static void qfloat_from_double_test(void)
+{
+    QFloat *qf;
+    const double value = -42.23423;
+
+    qf = qfloat_from_double(value);
+    g_assert(qf != NULL);
+    g_assert(qf->value == value);
+    g_assert(qf->base.refcnt == 1);
+    g_assert(qobject_type(QOBJECT(qf)) == QTYPE_QFLOAT);
+
+    // destroy doesn't exit yet
+    g_free(qf);
+}
+
+static void qfloat_destroy_test(void)
+{
+    QFloat *qf = qfloat_from_double(0.0);
+    QDECREF(qf);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/public/from_double", qfloat_from_double_test);
+    g_test_add_func("/public/destroy", qfloat_destroy_test);
+
+    return g_test_run();
+}
diff --git a/tests/check-qint.c b/tests/check-qint.c
new file mode 100644
index 0000000000..5a27119ae2
--- /dev/null
+++ b/tests/check-qint.c
@@ -0,0 +1,87 @@
+/*
+ * QInt unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 <glib.h>
+
+#include "qint.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+static void qint_from_int_test(void)
+{
+    QInt *qi;
+    const int value = -42;
+
+    qi = qint_from_int(value);
+    g_assert(qi != NULL);
+    g_assert(qi->value == value);
+    g_assert(qi->base.refcnt == 1);
+    g_assert(qobject_type(QOBJECT(qi)) == QTYPE_QINT);
+
+    // destroy doesn't exit yet
+    g_free(qi);
+}
+
+static void qint_destroy_test(void)
+{
+    QInt *qi = qint_from_int(0);
+    QDECREF(qi);
+}
+
+static void qint_from_int64_test(void)
+{
+    QInt *qi;
+    const int64_t value = 0x1234567890abcdefLL;
+
+    qi = qint_from_int(value);
+    g_assert((int64_t) qi->value == value);
+
+    QDECREF(qi);
+}
+
+static void qint_get_int_test(void)
+{
+    QInt *qi;
+    const int value = 123456;
+
+    qi = qint_from_int(value);
+    g_assert(qint_get_int(qi) == value);
+
+    QDECREF(qi);
+}
+
+static void qobject_to_qint_test(void)
+{
+    QInt *qi;
+
+    qi = qint_from_int(0);
+    g_assert(qobject_to_qint(QOBJECT(qi)) == qi);
+
+    QDECREF(qi);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/public/from_int", qint_from_int_test);
+    g_test_add_func("/public/destroy", qint_destroy_test);
+    g_test_add_func("/public/from_int64", qint_from_int64_test);
+    g_test_add_func("/public/get_int", qint_get_int_test);
+    g_test_add_func("/public/to_qint", qobject_to_qint_test);
+
+    return g_test_run();
+}
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
new file mode 100644
index 0000000000..526e25ef6d
--- /dev/null
+++ b/tests/check-qjson.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * 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 <glib.h>
+
+#include "qstring.h"
+#include "qint.h"
+#include "qdict.h"
+#include "qlist.h"
+#include "qfloat.h"
+#include "qbool.h"
+#include "qjson.h"
+
+#include "qemu-common.h"
+
+static void escaped_string(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        const char *decoded;
+        int skip;
+    } test_cases[] = {
+        { "\"\\b\"", "\b" },
+        { "\"\\f\"", "\f" },
+        { "\"\\n\"", "\n" },
+        { "\"\\r\"", "\r" },
+        { "\"\\t\"", "\t" },
+        { "\"/\"", "/" },
+        { "\"\\/\"", "/", .skip = 1 },
+        { "\"\\\\\"", "\\" },
+        { "\"\\\"\"", "\"" },
+        { "\"hello world \\\"embedded string\\\"\"",
+          "hello world \"embedded string\"" },
+        { "\"hello world\\nwith new line\"", "hello world\nwith new line" },
+        { "\"single byte utf-8 \\u0020\"", "single byte utf-8  ", .skip = 1 },
+        { "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" },
+        { "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" },
+        {}
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QSTRING);
+        
+        str = qobject_to_qstring(obj);
+        g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);
+
+        if (test_cases[i].skip == 0) {
+            str = qobject_to_json(obj);
+            g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded);
+            qobject_decref(obj);
+        }
+
+        QDECREF(str);
+    }
+}
+
+static void simple_string(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        const char *decoded;
+    } test_cases[] = {
+        { "\"hello world\"", "hello world" },
+        { "\"the quick brown fox jumped over the fence\"",
+          "the quick brown fox jumped over the fence" },
+        {}
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QSTRING);
+        
+        str = qobject_to_qstring(obj);
+        g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+
+        str = qobject_to_json(obj);
+        g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+
+        qobject_decref(obj);
+        
+        QDECREF(str);
+    }
+}
+
+static void single_quote_string(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        const char *decoded;
+    } test_cases[] = {
+        { "'hello world'", "hello world" },
+        { "'the quick brown fox \\' jumped over the fence'",
+          "the quick brown fox ' jumped over the fence" },
+        {}
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QSTRING);
+        
+        str = qobject_to_qstring(obj);
+        g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+
+        QDECREF(str);
+    }
+}
+
+static void vararg_string(void)
+{
+    int i;
+    struct {
+        const char *decoded;
+    } test_cases[] = {
+        { "hello world" },
+        { "the quick brown fox jumped over the fence" },
+        {}
+    };
+
+    for (i = 0; test_cases[i].decoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_jsonf("%s", test_cases[i].decoded);
+
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QSTRING);
+        
+        str = qobject_to_qstring(obj);
+        g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+
+        QDECREF(str);
+    }
+}
+
+static void simple_number(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        int64_t decoded;
+        int skip;
+    } test_cases[] = {
+        { "0", 0 },
+        { "1234", 1234 },
+        { "1", 1 },
+        { "-32", -32 },
+        { "-0", 0, .skip = 1 },
+        { },
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QInt *qint;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QINT);
+
+        qint = qobject_to_qint(obj);
+        g_assert(qint_get_int(qint) == test_cases[i].decoded);
+        if (test_cases[i].skip == 0) {
+            QString *str;
+
+            str = qobject_to_json(obj);
+            g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+            QDECREF(str);
+        }
+
+        QDECREF(qint);
+    }
+}
+
+static void float_number(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        double decoded;
+        int skip;
+    } test_cases[] = {
+        { "32.43", 32.43 },
+        { "0.222", 0.222 },
+        { "-32.12313", -32.12313 },
+        { "-32.20e-10", -32.20e-10, .skip = 1 },
+        { },
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QFloat *qfloat;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QFLOAT);
+
+        qfloat = qobject_to_qfloat(obj);
+        g_assert(qfloat_get_double(qfloat) == test_cases[i].decoded);
+
+        if (test_cases[i].skip == 0) {
+            QString *str;
+
+            str = qobject_to_json(obj);
+            g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+            QDECREF(str);
+        }
+
+        QDECREF(qfloat);
+    }
+}
+
+static void vararg_number(void)
+{
+    QObject *obj;
+    QInt *qint;
+    QFloat *qfloat;
+    int value = 0x2342;
+    int64_t value64 = 0x2342342343LL;
+    double valuef = 2.323423423;
+
+    obj = qobject_from_jsonf("%d", value);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QINT);
+
+    qint = qobject_to_qint(obj);
+    g_assert(qint_get_int(qint) == value);
+
+    QDECREF(qint);
+
+    obj = qobject_from_jsonf("%" PRId64, value64);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QINT);
+
+    qint = qobject_to_qint(obj);
+    g_assert(qint_get_int(qint) == value64);
+
+    QDECREF(qint);
+
+    obj = qobject_from_jsonf("%f", valuef);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QFLOAT);
+
+    qfloat = qobject_to_qfloat(obj);
+    g_assert(qfloat_get_double(qfloat) == valuef);
+
+    QDECREF(qfloat);
+}
+
+static void keyword_literal(void)
+{
+    QObject *obj;
+    QBool *qbool;
+    QString *str;
+
+    obj = qobject_from_json("true");
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QBOOL);
+
+    qbool = qobject_to_qbool(obj);
+    g_assert(qbool_get_int(qbool) != 0);
+
+    str = qobject_to_json(obj);
+    g_assert(strcmp(qstring_get_str(str), "true") == 0);
+    QDECREF(str);
+
+    QDECREF(qbool);
+
+    obj = qobject_from_json("false");
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QBOOL);
+
+    qbool = qobject_to_qbool(obj);
+    g_assert(qbool_get_int(qbool) == 0);
+
+    str = qobject_to_json(obj);
+    g_assert(strcmp(qstring_get_str(str), "false") == 0);
+    QDECREF(str);
+
+    QDECREF(qbool);
+
+    obj = qobject_from_jsonf("%i", false);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QBOOL);
+
+    qbool = qobject_to_qbool(obj);
+    g_assert(qbool_get_int(qbool) == 0);
+
+    QDECREF(qbool);
+    
+    obj = qobject_from_jsonf("%i", true);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QBOOL);
+
+    qbool = qobject_to_qbool(obj);
+    g_assert(qbool_get_int(qbool) != 0);
+
+    QDECREF(qbool);
+}
+
+typedef struct LiteralQDictEntry LiteralQDictEntry;
+typedef struct LiteralQObject LiteralQObject;
+
+struct LiteralQObject
+{
+    int type;
+    union {
+        int64_t qint;
+        const char *qstr;
+        LiteralQDictEntry *qdict;
+        LiteralQObject *qlist;
+    } value;
+};
+
+struct LiteralQDictEntry
+{
+    const char *key;
+    LiteralQObject value;
+};
+
+#define QLIT_QINT(val) (LiteralQObject){.type = QTYPE_QINT, .value.qint = (val)}
+#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
+#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
+#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
+
+typedef struct QListCompareHelper
+{
+    int index;
+    LiteralQObject *objs;
+    int result;
+} QListCompareHelper;
+
+static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
+
+static void compare_helper(QObject *obj, void *opaque)
+{
+    QListCompareHelper *helper = opaque;
+
+    if (helper->result == 0) {
+        return;
+    }
+
+    if (helper->objs[helper->index].type == QTYPE_NONE) {
+        helper->result = 0;
+        return;
+    }
+
+    helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
+}
+
+static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
+{
+    if (lhs->type != qobject_type(rhs)) {
+        return 0;
+    }
+
+    switch (lhs->type) {
+    case QTYPE_QINT:
+        return lhs->value.qint == qint_get_int(qobject_to_qint(rhs));
+    case QTYPE_QSTRING:
+        return (strcmp(lhs->value.qstr, qstring_get_str(qobject_to_qstring(rhs))) == 0);
+    case QTYPE_QDICT: {
+        int i;
+
+        for (i = 0; lhs->value.qdict[i].key; i++) {
+            QObject *obj = qdict_get(qobject_to_qdict(rhs), lhs->value.qdict[i].key);
+
+            if (!compare_litqobj_to_qobj(&lhs->value.qdict[i].value, obj)) {
+                return 0;
+            }
+        }
+
+        return 1;
+    }
+    case QTYPE_QLIST: {
+        QListCompareHelper helper;
+
+        helper.index = 0;
+        helper.objs = lhs->value.qlist;
+        helper.result = 1;
+        
+        qlist_iter(qobject_to_qlist(rhs), compare_helper, &helper);
+
+        return helper.result;
+    }
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+static void simple_dict(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        LiteralQObject decoded;
+    } test_cases[] = {
+        {
+            .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
+            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+                        { "foo", QLIT_QINT(42) },
+                        { "bar", QLIT_QSTR("hello world") },
+                        { }
+                    })),
+        }, {
+            .encoded = "{}",
+            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+                        { }
+                    })),
+        }, {
+            .encoded = "{\"foo\": 43}",
+            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+                        { "foo", QLIT_QINT(43) },
+                        { }
+                    })),
+        },
+        { }
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QDICT);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+        str = qobject_to_json(obj);
+        qobject_decref(obj);
+
+        obj = qobject_from_json(qstring_get_str(str));
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QDICT);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+        qobject_decref(obj);
+        QDECREF(str);
+    }
+}
+
+static void simple_list(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        LiteralQObject decoded;
+    } test_cases[] = {
+        {
+            .encoded = "[43,42]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        {
+            .encoded = "[43]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        { }
+                    })),
+        },
+        {
+            .encoded = "[]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        { }
+                    })),
+        },
+        {
+            .encoded = "[{}]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QDICT(((LiteralQDictEntry[]){
+                                    {},
+                                        })),
+                        {},
+                            })),
+        },
+        { }
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QLIST);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+        str = qobject_to_json(obj);
+        qobject_decref(obj);
+
+        obj = qobject_from_json(qstring_get_str(str));
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QLIST);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+        qobject_decref(obj);
+        QDECREF(str);
+    }
+}
+
+static void simple_whitespace(void)
+{
+    int i;
+    struct {
+        const char *encoded;
+        LiteralQObject decoded;
+    } test_cases[] = {
+        {
+            .encoded = " [ 43 , 42 ]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        {
+            .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QDICT(((LiteralQDictEntry[]){
+                                    { "h", QLIT_QSTR("b") },
+                                    { }})),
+                        QLIT_QLIST(((LiteralQObject[]){
+                                    { }})),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        {
+            .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
+            .decoded = QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(43),
+                        QLIT_QDICT(((LiteralQDictEntry[]){
+                                    { "h", QLIT_QSTR("b") },
+                                    { "a", QLIT_QINT(32) },
+                                    { }})),
+                        QLIT_QLIST(((LiteralQObject[]){
+                                    { }})),
+                        QLIT_QINT(42),
+                        { }
+                    })),
+        },
+        { }
+    };
+
+    for (i = 0; test_cases[i].encoded; i++) {
+        QObject *obj;
+        QString *str;
+
+        obj = qobject_from_json(test_cases[i].encoded);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QLIST);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+        str = qobject_to_json(obj);
+        qobject_decref(obj);
+
+        obj = qobject_from_json(qstring_get_str(str));
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QLIST);
+
+        g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+        qobject_decref(obj);
+        QDECREF(str);
+    }
+}
+
+static void simple_varargs(void)
+{
+    QObject *embedded_obj;
+    QObject *obj;
+    LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
+            QLIT_QINT(1),
+            QLIT_QINT(2),
+            QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QINT(32),
+                        QLIT_QINT(42),
+                        {}})),
+            {}}));
+
+    embedded_obj = qobject_from_json("[32, 42]");
+    g_assert(embedded_obj != NULL);
+
+    obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj);
+    g_assert(obj != NULL);
+
+    g_assert(compare_litqobj_to_qobj(&decoded, obj) == 1);
+
+    qobject_decref(obj);
+}
+
+static void empty_input(void)
+{
+    const char *empty = "";
+
+    QObject *obj = qobject_from_json(empty);
+    g_assert(obj == NULL);
+}
+
+static void unterminated_string(void)
+{
+    QObject *obj = qobject_from_json("\"abc");
+    g_assert(obj == NULL);
+}
+
+static void unterminated_sq_string(void)
+{
+    QObject *obj = qobject_from_json("'abc");
+    g_assert(obj == NULL);
+}
+
+static void unterminated_escape(void)
+{
+    QObject *obj = qobject_from_json("\"abc\\\"");
+    g_assert(obj == NULL);
+}
+
+static void unterminated_array(void)
+{
+    QObject *obj = qobject_from_json("[32");
+    g_assert(obj == NULL);
+}
+
+static void unterminated_array_comma(void)
+{
+    QObject *obj = qobject_from_json("[32,");
+    g_assert(obj == NULL);
+}
+
+static void invalid_array_comma(void)
+{
+    QObject *obj = qobject_from_json("[32,}");
+    g_assert(obj == NULL);
+}
+
+static void unterminated_dict(void)
+{
+    QObject *obj = qobject_from_json("{'abc':32");
+    g_assert(obj == NULL);
+}
+
+static void unterminated_dict_comma(void)
+{
+    QObject *obj = qobject_from_json("{'abc':32,");
+    g_assert(obj == NULL);
+}
+
+static void invalid_dict_comma(void)
+{
+    QObject *obj = qobject_from_json("{'abc':32,}");
+    g_assert(obj == NULL);
+}
+
+static void unterminated_literal(void)
+{
+    QObject *obj = qobject_from_json("nul");
+    g_assert(obj == NULL);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/literals/string/simple", simple_string);
+    g_test_add_func("/literals/string/escaped", escaped_string);
+    g_test_add_func("/literals/string/single_quote", single_quote_string);
+    g_test_add_func("/literals/string/vararg", vararg_string);
+
+    g_test_add_func("/literals/number/simple", simple_number);
+    g_test_add_func("/literals/number/float", float_number);
+    g_test_add_func("/literals/number/vararg", vararg_number);
+
+    g_test_add_func("/literals/keyword", keyword_literal);
+
+    g_test_add_func("/dicts/simple_dict", simple_dict);
+    g_test_add_func("/lists/simple_list", simple_list);
+
+    g_test_add_func("/whitespace/simple_whitespace", simple_whitespace);
+
+    g_test_add_func("/varargs/simple_varargs", simple_varargs);
+
+    g_test_add_func("/errors/empty_input", empty_input);
+    g_test_add_func("/errors/unterminated/string", unterminated_string);
+    g_test_add_func("/errors/unterminated/escape", unterminated_escape);
+    g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string);
+    g_test_add_func("/errors/unterminated/array", unterminated_array);
+    g_test_add_func("/errors/unterminated/array_comma", unterminated_array_comma);
+    g_test_add_func("/errors/unterminated/dict", unterminated_dict);
+    g_test_add_func("/errors/unterminated/dict_comma", unterminated_dict_comma);
+    g_test_add_func("/errors/invalid_array_comma", invalid_array_comma);
+    g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma);
+    g_test_add_func("/errors/unterminated/literal", unterminated_literal);
+
+    return g_test_run();
+}
diff --git a/tests/check-qlist.c b/tests/check-qlist.c
new file mode 100644
index 0000000000..501ba262da
--- /dev/null
+++ b/tests/check-qlist.c
@@ -0,0 +1,127 @@
+/*
+ * QList unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 <glib.h>
+
+#include "qint.h"
+#include "qlist.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+static void qlist_new_test(void)
+{
+    QList *qlist;
+
+    qlist = qlist_new();
+    g_assert(qlist != NULL);
+    g_assert(qlist->base.refcnt == 1);
+    g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST);
+
+    // destroy doesn't exist yet
+    g_free(qlist);
+}
+
+static void qlist_append_test(void)
+{
+    QInt *qi;
+    QList *qlist;
+    QListEntry *entry;
+
+    qi = qint_from_int(42);
+
+    qlist = qlist_new();
+    qlist_append(qlist, qi);
+
+    entry = QTAILQ_FIRST(&qlist->head);
+    g_assert(entry != NULL);
+    g_assert(entry->value == QOBJECT(qi));
+
+    // destroy doesn't exist yet
+    QDECREF(qi);
+    g_free(entry);
+    g_free(qlist);
+}
+
+static void qobject_to_qlist_test(void)
+{
+    QList *qlist;
+
+    qlist = qlist_new();
+
+    g_assert(qobject_to_qlist(QOBJECT(qlist)) == qlist);
+
+    // destroy doesn't exist yet
+    g_free(qlist);
+}
+
+static void qlist_destroy_test(void)
+{
+    int i;
+    QList *qlist;
+
+    qlist = qlist_new();
+
+    for (i = 0; i < 42; i++)
+        qlist_append(qlist, qint_from_int(i));
+
+    QDECREF(qlist);
+}
+
+static int iter_called;
+static const int iter_max = 42;
+
+static void iter_func(QObject *obj, void *opaque)
+{
+    QInt *qi;
+
+    g_assert(opaque == NULL);
+
+    qi = qobject_to_qint(obj);
+    g_assert(qi != NULL);
+    g_assert((qint_get_int(qi) >= 0) && (qint_get_int(qi) <= iter_max));
+
+    iter_called++;
+}
+
+static void qlist_iter_test(void)
+{
+    int i;
+    QList *qlist;
+
+    qlist = qlist_new();
+
+    for (i = 0; i < iter_max; i++)
+        qlist_append(qlist, qint_from_int(i));
+
+    iter_called = 0;
+    qlist_iter(qlist, iter_func, NULL);
+
+    g_assert(iter_called == iter_max);
+
+    QDECREF(qlist);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/public/new", qlist_new_test);
+    g_test_add_func("/public/append", qlist_append_test);
+    g_test_add_func("/public/to_qlist", qobject_to_qlist_test);
+    g_test_add_func("/public/destroy", qlist_destroy_test);
+    g_test_add_func("/public/iter", qlist_iter_test);
+
+    return g_test_run();
+}
diff --git a/tests/check-qstring.c b/tests/check-qstring.c
new file mode 100644
index 0000000000..addad6c673
--- /dev/null
+++ b/tests/check-qstring.c
@@ -0,0 +1,107 @@
+/*
+ * QString unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * 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 <glib.h>
+
+#include "qstring.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+static void qstring_from_str_test(void)
+{
+    QString *qstring;
+    const char *str = "QEMU";
+
+    qstring = qstring_from_str(str);
+    g_assert(qstring != NULL);
+    g_assert(qstring->base.refcnt == 1);
+    g_assert(strcmp(str, qstring->string) == 0);
+    g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
+
+    // destroy doesn't exit yet
+    g_free(qstring->string);
+    g_free(qstring);
+}
+
+static void qstring_destroy_test(void)
+{
+    QString *qstring = qstring_from_str("destroy test");
+    QDECREF(qstring);
+}
+
+static void qstring_get_str_test(void)
+{
+    QString *qstring;
+    const char *ret_str;
+    const char *str = "QEMU/KVM";
+
+    qstring = qstring_from_str(str);
+    ret_str = qstring_get_str(qstring);
+    g_assert(strcmp(ret_str, str) == 0);
+
+    QDECREF(qstring);
+}
+
+static void qstring_append_chr_test(void)
+{
+    int i;
+    QString *qstring;
+    const char *str = "qstring append char unit-test";
+
+    qstring = qstring_new();
+
+    for (i = 0; str[i]; i++)
+        qstring_append_chr(qstring, str[i]);
+
+    g_assert(strcmp(str, qstring_get_str(qstring)) == 0);
+    QDECREF(qstring);
+}
+
+static void qstring_from_substr_test(void)
+{
+    QString *qs;
+
+    qs = qstring_from_substr("virtualization", 3, 9);
+    g_assert(qs != NULL);
+    g_assert(strcmp(qstring_get_str(qs), "tualiza") == 0);
+
+    QDECREF(qs);
+}
+
+
+static void qobject_to_qstring_test(void)
+{
+    QString *qstring;
+
+    qstring = qstring_from_str("foo");
+    g_assert(qobject_to_qstring(QOBJECT(qstring)) == qstring);
+
+    QDECREF(qstring);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/public/from_str", qstring_from_str_test);
+    g_test_add_func("/public/destroy", qstring_destroy_test);
+    g_test_add_func("/public/get_str", qstring_get_str_test);
+    g_test_add_func("/public/append_chr", qstring_append_chr_test);
+    g_test_add_func("/public/from_substr", qstring_from_substr_test);
+    g_test_add_func("/public/to_qstring", qobject_to_qstring_test);
+
+    return g_test_run();
+}
diff --git a/tests/libqtest.c b/tests/libqtest.c
new file mode 100644
index 0000000000..d47969eae5
--- /dev/null
+++ b/tests/libqtest.c
@@ -0,0 +1,387 @@
+/*
+ * QTest
+ *
+ * Copyright IBM, Corp. 2012
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Paolo Bonzini     <pbonzini@redhat.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 "libqtest.h"
+
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "osdep.h"
+
+#define MAX_IRQ 256
+
+QTestState *global_qtest;
+
+struct QTestState
+{
+    int fd;
+    bool irq_level[MAX_IRQ];
+    GString *rx;
+    gchar *pid_file;
+};
+
+#define g_assert_no_errno(ret) do { \
+    g_assert_cmpint(ret, !=, -1); \
+} while (0)
+
+QTestState *qtest_init(const char *extra_args)
+{
+    QTestState *s;
+    struct sockaddr_un addr;
+    int sock, ret, i;
+    gchar *socket_path;
+    gchar *pid_file;
+    gchar *command;
+    const char *qemu_binary;
+    pid_t pid;
+    socklen_t addrlen;
+
+    qemu_binary = getenv("QTEST_QEMU_BINARY");
+    g_assert(qemu_binary != NULL);
+
+    socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
+    pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
+
+    s = g_malloc(sizeof(*s));
+
+    sock = socket(PF_UNIX, SOCK_STREAM, 0);
+    g_assert_no_errno(sock);
+
+    addr.sun_family = AF_UNIX;
+    snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
+    qemu_set_cloexec(sock);
+
+    do {
+        ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
+    } while (ret == -1 && errno == EINTR);
+    g_assert_no_errno(ret);
+    listen(sock, 1);
+
+    pid = fork();
+    if (pid == 0) {
+        command = g_strdup_printf("%s "
+                                  "-qtest unix:%s,nowait "
+                                  "-qtest-log /dev/null "
+                                  "-pidfile %s "
+                                  "-machine accel=qtest "
+                                  "%s", qemu_binary, socket_path,
+                                  pid_file,
+                                  extra_args ?: "");
+
+        ret = system(command);
+        exit(ret);
+        g_free(command);
+    }
+
+    do {
+        ret = accept(sock, (struct sockaddr *)&addr, &addrlen);
+    } while (ret == -1 && errno == EINTR);
+    g_assert_no_errno(ret);
+    close(sock);
+
+    s->fd = ret;
+    s->rx = g_string_new("");
+    s->pid_file = pid_file;
+    for (i = 0; i < MAX_IRQ; i++) {
+        s->irq_level[i] = false;
+    }
+
+    g_free(socket_path);
+
+    return s;
+}
+
+void qtest_quit(QTestState *s)
+{
+    FILE *f;
+    char buffer[1024];
+
+    f = fopen(s->pid_file, "r");
+    if (f) {
+        if (fgets(buffer, sizeof(buffer), f)) {
+            pid_t pid = atoi(buffer);
+            int status = 0;
+
+            kill(pid, SIGTERM);
+            waitpid(pid, &status, 0);
+        }
+
+        fclose(f);
+    }
+}
+
+static void qtest_sendf(QTestState *s, const char *fmt, ...)
+{
+    va_list ap;
+    gchar *str;
+    size_t size, offset;
+
+    va_start(ap, fmt);
+    str = g_strdup_vprintf(fmt, ap);
+    va_end(ap);
+    size = strlen(str);
+
+    offset = 0;
+    while (offset < size) {
+        ssize_t len;
+
+        len = write(s->fd, str + offset, size - offset);
+        if (len == -1 && errno == EINTR) {
+            continue;
+        }
+
+        g_assert_no_errno(len);
+        g_assert_cmpint(len, >, 0);
+
+        offset += len;
+    }
+}
+
+static GString *qtest_recv_line(QTestState *s)
+{
+    GString *line;
+    size_t offset;
+    char *eol;
+
+    while ((eol = strchr(s->rx->str, '\n')) == NULL) {
+        ssize_t len;
+        char buffer[1024];
+
+        len = read(s->fd, buffer, sizeof(buffer));
+        if (len == -1 && errno == EINTR) {
+            continue;
+        }
+
+        if (len == -1 || len == 0) {
+            fprintf(stderr, "Broken pipe\n");
+            exit(1);
+        }
+
+        g_string_append_len(s->rx, buffer, len);
+    }
+
+    offset = eol - s->rx->str;
+    line = g_string_new_len(s->rx->str, offset);
+    g_string_erase(s->rx, 0, offset + 1);
+
+    return line;
+}
+
+static gchar **qtest_rsp(QTestState *s, int expected_args)
+{
+    GString *line;
+    gchar **words;
+    int i;
+
+redo:
+    line = qtest_recv_line(s);
+    words = g_strsplit(line->str, " ", 0);
+    g_string_free(line, TRUE);
+
+    if (strcmp(words[0], "IRQ") == 0) {
+        int irq;
+
+        g_assert(words[1] != NULL);
+        g_assert(words[2] != NULL);
+
+        irq = strtoul(words[2], NULL, 0);
+        g_assert_cmpint(irq, >=, 0);
+        g_assert_cmpint(irq, <, MAX_IRQ);
+
+        if (strcmp(words[1], "raise") == 0) {
+            s->irq_level[irq] = true;
+        } else {
+            s->irq_level[irq] = false;
+        }
+
+        g_strfreev(words);
+        goto redo;
+    }
+
+    g_assert(words[0] != NULL);
+    g_assert_cmpstr(words[0], ==, "OK");
+
+    if (expected_args) {
+        for (i = 0; i < expected_args; i++) {
+            g_assert(words[i] != NULL);
+        }
+    } else {
+        g_strfreev(words);
+    }
+
+    return words;
+}
+
+const char *qtest_get_arch(void)
+{
+    const char *qemu = getenv("QTEST_QEMU_BINARY");
+    const char *end = strrchr(qemu, '/');
+
+    return end + strlen("/qemu-system-");
+}
+
+bool qtest_get_irq(QTestState *s, int num)
+{
+    /* dummy operation in order to make sure irq is up to date */
+    qtest_inb(s, 0);
+
+    return s->irq_level[num];
+}
+
+static int64_t qtest_clock_rsp(QTestState *s)
+{
+    gchar **words;
+    int64_t clock;
+    words = qtest_rsp(s, 2);
+    clock = g_ascii_strtoll(words[1], NULL, 0);
+    g_strfreev(words);
+    return clock;
+}
+
+int64_t qtest_clock_step_next(QTestState *s)
+{
+    qtest_sendf(s, "clock_step\n");
+    return qtest_clock_rsp(s);
+}
+
+int64_t qtest_clock_step(QTestState *s, int64_t step)
+{
+    qtest_sendf(s, "clock_step %"PRIi64"\n", step);
+    return qtest_clock_rsp(s);
+}
+
+int64_t qtest_clock_set(QTestState *s, int64_t val)
+{
+    qtest_sendf(s, "clock_set %"PRIi64"\n", val);
+    return qtest_clock_rsp(s);
+}
+
+void qtest_irq_intercept_out(QTestState *s, const char *qom_path)
+{
+    qtest_sendf(s, "irq_intercept_out %s\n", qom_path);
+    qtest_rsp(s, 0);
+}
+
+void qtest_irq_intercept_in(QTestState *s, const char *qom_path)
+{
+    qtest_sendf(s, "irq_intercept_in %s\n", qom_path);
+    qtest_rsp(s, 0);
+}
+
+static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value)
+{
+    qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value);
+    qtest_rsp(s, 0);
+}
+
+void qtest_outb(QTestState *s, uint16_t addr, uint8_t value)
+{
+    qtest_out(s, "outb", addr, value);
+}
+
+void qtest_outw(QTestState *s, uint16_t addr, uint16_t value)
+{
+    qtest_out(s, "outw", addr, value);
+}
+
+void qtest_outl(QTestState *s, uint16_t addr, uint32_t value)
+{
+    qtest_out(s, "outl", addr, value);
+}
+
+static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr)
+{
+    gchar **args;
+    uint32_t value;
+
+    qtest_sendf(s, "%s 0x%x\n", cmd, addr);
+    args = qtest_rsp(s, 2);
+    value = strtoul(args[1], NULL, 0);
+    g_strfreev(args);
+
+    return value;
+}
+
+uint8_t qtest_inb(QTestState *s, uint16_t addr)
+{
+    return qtest_in(s, "inb", addr);
+}
+
+uint16_t qtest_inw(QTestState *s, uint16_t addr)
+{
+    return qtest_in(s, "inw", addr);
+}
+
+uint32_t qtest_inl(QTestState *s, uint16_t addr)
+{
+    return qtest_in(s, "inl", addr);
+}
+
+static int hex2nib(char ch)
+{
+    if (ch >= '0' && ch <= '9') {
+        return ch - '0';
+    } else if (ch >= 'a' && ch <= 'f') {
+        return 10 + (ch - 'a');
+    } else if (ch >= 'A' && ch <= 'F') {
+        return 10 + (ch - 'a');
+    } else {
+        return -1;
+    }
+}
+
+void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size)
+{
+    uint8_t *ptr = data;
+    gchar **args;
+    size_t i;
+
+    qtest_sendf(s, "read 0x%x 0x%x\n", addr, size);
+    args = qtest_rsp(s, 2);
+
+    for (i = 0; i < size; i++) {
+        ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4;
+        ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]);
+    }
+
+    g_strfreev(args);
+}
+
+void qtest_add_func(const char *str, void (*fn))
+{
+    gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str);
+    g_test_add_func(path, fn);
+}
+
+void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
+{
+    const uint8_t *ptr = data;
+    size_t i;
+
+    qtest_sendf(s, "write 0x%x 0x%x 0x", addr, size);
+    for (i = 0; i < size; i++) {
+        qtest_sendf(s, "%02x", ptr[i]);
+    }
+    qtest_sendf(s, "\n");
+    qtest_rsp(s, 0);
+}
diff --git a/tests/libqtest.h b/tests/libqtest.h
new file mode 100644
index 0000000000..2ca85a9ee9
--- /dev/null
+++ b/tests/libqtest.h
@@ -0,0 +1,335 @@
+/*
+ * QTest
+ *
+ * Copyright IBM, Corp. 2012
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Paolo Bonzini     <pbonzini@redhat.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 LIBQTEST_H
+#define LIBQTEST_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct QTestState QTestState;
+
+extern QTestState *global_qtest;
+
+/**
+ * qtest_init:
+ * @extra_args: other arguments to pass to QEMU.
+ */
+QTestState *qtest_init(const char *extra_args);
+
+/**
+ * qtest_quit:
+ * @s: QTestState instance to operate on.
+ *
+ * Shut down the QEMU process associated to @s.
+ */
+void qtest_quit(QTestState *s);
+
+/**
+ * qtest_get_irq:
+ * @s: QTestState instance to operate on.
+ * @num: Interrupt to observe.
+ *
+ * Return the level of the @num interrupt.
+ */
+bool qtest_get_irq(QTestState *s, int num);
+
+/**
+ * qtest_irq_intercept_in:
+ * @s: QTestState instance to operate on.
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-in pins of the device
+ * whose path is specified by @string.
+ */
+void qtest_irq_intercept_in(QTestState *s, const char *string);
+
+/**
+ * qtest_irq_intercept_out:
+ * @s: QTestState instance to operate on.
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-out pins of the device
+ * whose path is specified by @string.
+ */
+void qtest_irq_intercept_out(QTestState *s, const char *string);
+
+/**
+ * qtest_outb:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write an 8-bit value to an I/O port.
+ */
+void qtest_outb(QTestState *s, uint16_t addr, uint8_t value);
+
+/**
+ * qtest_outw:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 16-bit value to an I/O port.
+ */
+void qtest_outw(QTestState *s, uint16_t addr, uint16_t value);
+
+/**
+ * qtest_outl:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 32-bit value to an I/O port.
+ */
+void qtest_outl(QTestState *s, uint16_t addr, uint32_t value);
+
+/**
+ * qtest_inb:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns an 8-bit value from an I/O port.
+ */
+uint8_t qtest_inb(QTestState *s, uint16_t addr);
+
+/**
+ * qtest_inw:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 16-bit value from an I/O port.
+ */
+uint16_t qtest_inw(QTestState *s, uint16_t addr);
+
+/**
+ * qtest_inl:
+ * @s: QTestState instance to operate on.
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 32-bit value from an I/O port.
+ */
+uint32_t qtest_inl(QTestState *s, uint16_t addr);
+
+/**
+ * qtest_memread:
+ * @s: QTestState instance to operate on.
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ *
+ * Read guest memory into a buffer.
+ */
+void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size);
+
+/**
+ * qtest_memwrite:
+ * @s: QTestState instance to operate on.
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ *
+ * Write a buffer to guest memory.
+ */
+void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size);
+
+/**
+ * qtest_clock_step_next:
+ * @s: QTestState instance to operate on.
+ *
+ * Advance the vm_clock to the next deadline.  Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+int64_t qtest_clock_step_next(QTestState *s);
+
+/**
+ * qtest_clock_step:
+ * @s: QTestState instance to operate on.
+ * @step: Number of nanoseconds to advance the clock by.
+ *
+ * Advance the vm_clock by @step nanoseconds.  Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+int64_t qtest_clock_step(QTestState *s, int64_t step);
+
+/**
+ * qtest_clock_set:
+ * @s: QTestState instance to operate on.
+ * @val: Nanoseconds value to advance the clock to.
+ *
+ * Advance the vm_clock to @val nanoseconds since the VM was launched.
+ * Return the current value of the vm_clock in nanoseconds.
+ */
+int64_t qtest_clock_set(QTestState *s, int64_t val);
+
+/**
+ * qtest_get_arch:
+ *
+ * Returns the architecture for the QEMU executable under test.
+ */
+const char *qtest_get_arch(void);
+
+/**
+ * qtest_add_func:
+ * @str: Test case path.
+ * @fn: Test case function
+ *
+ * Add a GTester testcase with the given name and function.
+ * The path is prefixed with the architecture under test, as
+ * returned by qtest_get_arch.
+ */
+void qtest_add_func(const char *str, void (*fn));
+
+/**
+ * qtest_start:
+ * @args: other arguments to pass to QEMU
+ *
+ * Start QEMU and assign the resulting QTestState to a global variable.
+ * The global variable is used by "shortcut" macros documented below.
+ */
+#define qtest_start(args) (            \
+    global_qtest = qtest_init((args)) \
+        )
+
+/**
+ * get_irq:
+ * @num: Interrupt to observe.
+ *
+ * Return the level of the @num interrupt.
+ */
+#define get_irq(num) qtest_get_irq(global_qtest, num)
+
+/**
+ * irq_intercept_in:
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-in pins of the device
+ * whose path is specified by @string.
+ */
+#define irq_intercept_in(string) qtest_irq_intercept_in(global_qtest, string)
+
+/**
+ * qtest_irq_intercept_out:
+ * @string: QOM path of a device.
+ *
+ * Associate qtest irqs with the GPIO-out pins of the device
+ * whose path is specified by @string.
+ */
+#define irq_intercept_out(string) qtest_irq_intercept_out(global_qtest, string)
+
+/**
+ * outb:
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write an 8-bit value to an I/O port.
+ */
+#define outb(addr, val) qtest_outb(global_qtest, addr, val)
+
+/**
+ * outw:
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 16-bit value to an I/O port.
+ */
+#define outw(addr, val) qtest_outw(global_qtest, addr, val)
+
+/**
+ * outl:
+ * @addr: I/O port to write to.
+ * @value: Value being written.
+ *
+ * Write a 32-bit value to an I/O port.
+ */
+#define outl(addr, val) qtest_outl(global_qtest, addr, val)
+
+/**
+ * inb:
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns an 8-bit value from an I/O port.
+ */
+#define inb(addr) qtest_inb(global_qtest, addr)
+
+/**
+ * inw:
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 16-bit value from an I/O port.
+ */
+#define inw(addr) qtest_inw(global_qtest, addr)
+
+/**
+ * inl:
+ * @addr: I/O port to read from.
+ * @value: Value being written.
+ *
+ * Returns a 32-bit value from an I/O port.
+ */
+#define inl(addr) qtest_inl(global_qtest, addr)
+
+/**
+ * memread:
+ * @addr: Guest address to read from.
+ * @data: Pointer to where memory contents will be stored.
+ * @size: Number of bytes to read.
+ *
+ * Read guest memory into a buffer.
+ */
+#define memread(addr, data, size) qtest_memread(global_qtest, addr, data, size)
+
+/**
+ * memwrite:
+ * @addr: Guest address to write to.
+ * @data: Pointer to the bytes that will be written to guest memory.
+ * @size: Number of bytes to write.
+ *
+ * Write a buffer to guest memory.
+ */
+#define memwrite(addr, data, size) qtest_memwrite(global_qtest, addr, data, size)
+
+/**
+ * clock_step_next:
+ *
+ * Advance the vm_clock to the next deadline.  Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+#define clock_step_next() qtest_clock_step_next(global_qtest)
+
+/**
+ * clock_step:
+ * @step: Number of nanoseconds to advance the clock by.
+ *
+ * Advance the vm_clock by @step nanoseconds.  Return the current
+ * value of the vm_clock in nanoseconds.
+ */
+#define clock_step(step) qtest_clock_step(global_qtest, step)
+
+/**
+ * clock_set:
+ * @val: Nanoseconds value to advance the clock to.
+ *
+ * Advance the vm_clock to @val nanoseconds since the VM was launched.
+ * Return the current value of the vm_clock in nanoseconds.
+ */
+#define clock_set(val) qtest_clock_set(global_qtest, val)
+
+#endif
diff --git a/tests/rtc-test.c b/tests/rtc-test.c
new file mode 100644
index 0000000000..983a980bab
--- /dev/null
+++ b/tests/rtc-test.c
@@ -0,0 +1,263 @@
+/*
+ * QTest testcase for the MC146818 real-time clock
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.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 "libqtest.h"
+#include "hw/mc146818rtc_regs.h"
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static uint8_t base = 0x70;
+
+static int bcd2dec(int value)
+{
+    return (((value >> 4) & 0x0F) * 10) + (value & 0x0F);
+}
+
+static int dec2bcd(int value)
+{
+    return ((value / 10) << 4) | (value % 10);
+}
+
+static uint8_t cmos_read(uint8_t reg)
+{
+    outb(base + 0, reg);
+    return inb(base + 1);
+}
+
+static void cmos_write(uint8_t reg, uint8_t val)
+{
+    outb(base + 0, reg);
+    outb(base + 1, val);
+}
+
+static int tm_cmp(struct tm *lhs, struct tm *rhs)
+{
+    time_t a, b;
+    struct tm d1, d2;
+
+    memcpy(&d1, lhs, sizeof(d1));
+    memcpy(&d2, rhs, sizeof(d2));
+
+    a = mktime(&d1);
+    b = mktime(&d2);
+
+    if (a < b) {
+        return -1;
+    } else if (a > b) {
+        return 1;
+    }
+
+    return 0;
+}
+
+#if 0
+static void print_tm(struct tm *tm)
+{
+    printf("%04d-%02d-%02d %02d:%02d:%02d\n",
+           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+           tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff);
+}
+#endif
+
+static void cmos_get_date_time(struct tm *date)
+{
+    int base_year = 2000, hour_offset;
+    int sec, min, hour, mday, mon, year;
+    time_t ts;
+    struct tm dummy;
+
+    sec = cmos_read(RTC_SECONDS);
+    min = cmos_read(RTC_MINUTES);
+    hour = cmos_read(RTC_HOURS);
+    mday = cmos_read(RTC_DAY_OF_MONTH);
+    mon = cmos_read(RTC_MONTH);
+    year = cmos_read(RTC_YEAR);
+
+    if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) {
+        sec = bcd2dec(sec);
+        min = bcd2dec(min);
+        hour = bcd2dec(hour);
+        mday = bcd2dec(mday);
+        mon = bcd2dec(mon);
+        year = bcd2dec(year);
+        hour_offset = 80;
+    } else {
+        hour_offset = 0x80;
+    }
+
+    if ((cmos_read(0x0B) & REG_B_24H) == 0) {
+        if (hour >= hour_offset) {
+            hour -= hour_offset;
+            hour += 12;
+        }
+    }
+
+    ts = time(NULL);
+    localtime_r(&ts, &dummy);
+
+    date->tm_isdst = dummy.tm_isdst;
+    date->tm_sec = sec;
+    date->tm_min = min;
+    date->tm_hour = hour;
+    date->tm_mday = mday;
+    date->tm_mon = mon - 1;
+    date->tm_year = base_year + year - 1900;
+    date->tm_gmtoff = 0;
+
+    ts = mktime(date);
+}
+
+static void check_time(int wiggle)
+{
+    struct tm start, date[4], end;
+    struct tm *datep;
+    time_t ts;
+
+    /*
+     * This check assumes a few things.  First, we cannot guarantee that we get
+     * a consistent reading from the wall clock because we may hit an edge of
+     * the clock while reading.  To work around this, we read four clock readings
+     * such that at least two of them should match.  We need to assume that one
+     * reading is corrupt so we need four readings to ensure that we have at
+     * least two consecutive identical readings
+     *
+     * It's also possible that we'll cross an edge reading the host clock so
+     * simply check to make sure that the clock reading is within the period of
+     * when we expect it to be.
+     */
+
+    ts = time(NULL);
+    gmtime_r(&ts, &start);
+
+    cmos_get_date_time(&date[0]);
+    cmos_get_date_time(&date[1]);
+    cmos_get_date_time(&date[2]);
+    cmos_get_date_time(&date[3]);
+
+    ts = time(NULL);
+    gmtime_r(&ts, &end);
+
+    if (tm_cmp(&date[0], &date[1]) == 0) {
+        datep = &date[0];
+    } else if (tm_cmp(&date[1], &date[2]) == 0) {
+        datep = &date[1];
+    } else if (tm_cmp(&date[2], &date[3]) == 0) {
+        datep = &date[2];
+    } else {
+        g_assert_not_reached();
+    }
+
+    if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
+        long t, s;
+
+        start.tm_isdst = datep->tm_isdst;
+
+        t = (long)mktime(datep);
+        s = (long)mktime(&start);
+        if (t < s) {
+            g_test_message("RTC is %ld second(s) behind wall-clock\n", (s - t));
+        } else {
+            g_test_message("RTC is %ld second(s) ahead of wall-clock\n", (t - s));
+        }
+
+        g_assert_cmpint(ABS(t - s), <=, wiggle);
+    }
+}
+
+static int wiggle = 2;
+
+static void bcd_check_time(void)
+{
+    /* Set BCD mode */
+    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM);
+    check_time(wiggle);
+}
+
+static void dec_check_time(void)
+{
+    /* Set DEC mode */
+    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM);
+    check_time(wiggle);
+}
+
+static void set_alarm_time(struct tm *tm)
+{
+    int sec;
+
+    sec = tm->tm_sec;
+
+    if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) {
+        sec = dec2bcd(sec);
+    }
+
+    cmos_write(RTC_SECONDS_ALARM, sec);
+    cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
+    cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
+}
+
+static void alarm_time(void)
+{
+    struct tm now;
+    time_t ts;
+    int i;
+
+    ts = time(NULL);
+    gmtime_r(&ts, &now);
+
+    /* set DEC mode */
+    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM);
+
+    g_assert(!get_irq(RTC_ISA_IRQ));
+    cmos_read(RTC_REG_C);
+
+    now.tm_sec = (now.tm_sec + 2) % 60;
+    set_alarm_time(&now);
+    cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE);
+
+    for (i = 0; i < 2 + wiggle; i++) {
+        if (get_irq(RTC_ISA_IRQ)) {
+            break;
+        }
+
+        clock_step(1000000000);
+    }
+
+    g_assert(get_irq(RTC_ISA_IRQ));
+    g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
+    g_assert(cmos_read(RTC_REG_C) == 0);
+}
+
+int main(int argc, char **argv)
+{
+    QTestState *s = NULL;
+    int ret;
+
+    g_test_init(&argc, &argv, NULL);
+
+    s = qtest_start("-display none -rtc clock=vm");
+    qtest_irq_intercept_in(s, "ioapic");
+
+    qtest_add_func("/rtc/bcd/check-time", bcd_check_time);
+    qtest_add_func("/rtc/dec/check-time", dec_check_time);
+    qtest_add_func("/rtc/alarm-time", alarm_time);
+    ret = g_test_run();
+
+    if (s) {
+        qtest_quit(s);
+    }
+
+    return ret;
+}
diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c
new file mode 100644
index 0000000000..e5d14eb696
--- /dev/null
+++ b/tests/test-coroutine.c
@@ -0,0 +1,219 @@
+/*
+ * Coroutine tests
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <glib.h>
+#include "qemu-coroutine.h"
+
+/*
+ * Check that qemu_in_coroutine() works
+ */
+
+static void coroutine_fn verify_in_coroutine(void *opaque)
+{
+    g_assert(qemu_in_coroutine());
+}
+
+static void test_in_coroutine(void)
+{
+    Coroutine *coroutine;
+
+    g_assert(!qemu_in_coroutine());
+
+    coroutine = qemu_coroutine_create(verify_in_coroutine);
+    qemu_coroutine_enter(coroutine, NULL);
+}
+
+/*
+ * Check that qemu_coroutine_self() works
+ */
+
+static void coroutine_fn verify_self(void *opaque)
+{
+    g_assert(qemu_coroutine_self() == opaque);
+}
+
+static void test_self(void)
+{
+    Coroutine *coroutine;
+
+    coroutine = qemu_coroutine_create(verify_self);
+    qemu_coroutine_enter(coroutine, coroutine);
+}
+
+/*
+ * Check that coroutines may nest multiple levels
+ */
+
+typedef struct {
+    unsigned int n_enter;   /* num coroutines entered */
+    unsigned int n_return;  /* num coroutines returned */
+    unsigned int max;       /* maximum level of nesting */
+} NestData;
+
+static void coroutine_fn nest(void *opaque)
+{
+    NestData *nd = opaque;
+
+    nd->n_enter++;
+
+    if (nd->n_enter < nd->max) {
+        Coroutine *child;
+
+        child = qemu_coroutine_create(nest);
+        qemu_coroutine_enter(child, nd);
+    }
+
+    nd->n_return++;
+}
+
+static void test_nesting(void)
+{
+    Coroutine *root;
+    NestData nd = {
+        .n_enter  = 0,
+        .n_return = 0,
+        .max      = 128,
+    };
+
+    root = qemu_coroutine_create(nest);
+    qemu_coroutine_enter(root, &nd);
+
+    /* Must enter and return from max nesting level */
+    g_assert_cmpint(nd.n_enter, ==, nd.max);
+    g_assert_cmpint(nd.n_return, ==, nd.max);
+}
+
+/*
+ * Check that yield/enter transfer control correctly
+ */
+
+static void coroutine_fn yield_5_times(void *opaque)
+{
+    bool *done = opaque;
+    int i;
+
+    for (i = 0; i < 5; i++) {
+        qemu_coroutine_yield();
+    }
+    *done = true;
+}
+
+static void test_yield(void)
+{
+    Coroutine *coroutine;
+    bool done = false;
+    int i = -1; /* one extra time to return from coroutine */
+
+    coroutine = qemu_coroutine_create(yield_5_times);
+    while (!done) {
+        qemu_coroutine_enter(coroutine, &done);
+        i++;
+    }
+    g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
+}
+
+/*
+ * Check that creation, enter, and return work
+ */
+
+static void coroutine_fn set_and_exit(void *opaque)
+{
+    bool *done = opaque;
+
+    *done = true;
+}
+
+static void test_lifecycle(void)
+{
+    Coroutine *coroutine;
+    bool done = false;
+
+    /* Create, enter, and return from coroutine */
+    coroutine = qemu_coroutine_create(set_and_exit);
+    qemu_coroutine_enter(coroutine, &done);
+    g_assert(done); /* expect done to be true (first time) */
+
+    /* Repeat to check that no state affects this test */
+    done = false;
+    coroutine = qemu_coroutine_create(set_and_exit);
+    qemu_coroutine_enter(coroutine, &done);
+    g_assert(done); /* expect done to be true (second time) */
+}
+
+/*
+ * Lifecycle benchmark
+ */
+
+static void coroutine_fn empty_coroutine(void *opaque)
+{
+    /* Do nothing */
+}
+
+static void perf_lifecycle(void)
+{
+    Coroutine *coroutine;
+    unsigned int i, max;
+    double duration;
+
+    max = 1000000;
+
+    g_test_timer_start();
+    for (i = 0; i < max; i++) {
+        coroutine = qemu_coroutine_create(empty_coroutine);
+        qemu_coroutine_enter(coroutine, NULL);
+    }
+    duration = g_test_timer_elapsed();
+
+    g_test_message("Lifecycle %u iterations: %f s\n", max, duration);
+}
+
+static void perf_nesting(void)
+{
+    unsigned int i, maxcycles, maxnesting;
+    double duration;
+
+    maxcycles = 100000000;
+    maxnesting = 20000;
+    Coroutine *root;
+    NestData nd = {
+        .n_enter  = 0,
+        .n_return = 0,
+        .max      = maxnesting,
+    };
+
+    g_test_timer_start();
+    for (i = 0; i < maxcycles; i++) {
+        root = qemu_coroutine_create(nest);
+        qemu_coroutine_enter(root, &nd);
+    }
+    duration = g_test_timer_elapsed();
+
+    g_test_message("Nesting %u iterations of %u depth each: %f s\n",
+        maxcycles, maxnesting, duration);
+}
+
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    g_test_add_func("/basic/lifecycle", test_lifecycle);
+    g_test_add_func("/basic/yield", test_yield);
+    g_test_add_func("/basic/nesting", test_nesting);
+    g_test_add_func("/basic/self", test_self);
+    g_test_add_func("/basic/in_coroutine", test_in_coroutine);
+    if (g_test_perf()) {
+        g_test_add_func("/perf/lifecycle", perf_lifecycle);
+        g_test_add_func("/perf/nesting", perf_nesting);
+    }
+    return g_test_run();
+}
diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c
new file mode 100644
index 0000000000..60cbf019bb
--- /dev/null
+++ b/tests/test-qmp-commands.c
@@ -0,0 +1,139 @@
+#include <glib.h>
+#include "qemu-objects.h"
+#include "test-qmp-commands.h"
+#include "qapi/qmp-core.h"
+#include "module.h"
+
+void qmp_user_def_cmd(Error **errp)
+{
+}
+
+void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
+{
+}
+
+UserDefTwo * qmp_user_def_cmd2(UserDefOne * ud1a, UserDefOne * ud1b, Error **errp)
+{
+    UserDefTwo *ret;
+    UserDefOne *ud1c = g_malloc0(sizeof(UserDefOne));
+    UserDefOne *ud1d = g_malloc0(sizeof(UserDefOne));
+
+    ud1c->string = strdup(ud1a->string);
+    ud1c->integer = ud1a->integer;
+    ud1d->string = strdup(ud1b->string);
+    ud1d->integer = ud1b->integer;
+
+    ret = g_malloc0(sizeof(UserDefTwo));
+    ret->string = strdup("blah1");
+    ret->dict.string = strdup("blah2");
+    ret->dict.dict.userdef = ud1c;
+    ret->dict.dict.string = strdup("blah3");
+    ret->dict.has_dict2 = true;
+    ret->dict.dict2.userdef = ud1d;
+    ret->dict.dict2.string = strdup("blah4");
+
+    return ret;
+}
+
+/* test commands with no input and no return value */
+static void test_dispatch_cmd(void)
+{
+    QDict *req = qdict_new();
+    QObject *resp;
+
+    qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
+
+    resp = qmp_dispatch(QOBJECT(req));
+    assert(resp != NULL);
+    assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+
+    qobject_decref(resp);
+    QDECREF(req);
+}
+
+/* test commands that return an error due to invalid parameters */
+static void test_dispatch_cmd_error(void)
+{
+    QDict *req = qdict_new();
+    QObject *resp;
+
+    qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+
+    resp = qmp_dispatch(QOBJECT(req));
+    assert(resp != NULL);
+    assert(qdict_haskey(qobject_to_qdict(resp), "error"));
+
+    qobject_decref(resp);
+    QDECREF(req);
+}
+
+/* test commands that involve both input parameters and return values */
+static void test_dispatch_cmd_io(void)
+{
+    QDict *req = qdict_new();
+    QDict *args = qdict_new();
+    QDict *ud1a = qdict_new();
+    QDict *ud1b = qdict_new();
+    QObject *resp;
+
+    qdict_put_obj(ud1a, "integer", QOBJECT(qint_from_int(42)));
+    qdict_put_obj(ud1a, "string", QOBJECT(qstring_from_str("hello")));
+    qdict_put_obj(ud1b, "integer", QOBJECT(qint_from_int(422)));
+    qdict_put_obj(ud1b, "string", QOBJECT(qstring_from_str("hello2")));
+    qdict_put_obj(args, "ud1a", QOBJECT(ud1a));
+    qdict_put_obj(args, "ud1b", QOBJECT(ud1b));
+    qdict_put_obj(req, "arguments", QOBJECT(args));
+
+    qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
+
+    /* TODO: put in full payload and check for errors */
+    resp = qmp_dispatch(QOBJECT(req));
+    assert(resp != NULL);
+    assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
+
+    qobject_decref(resp);
+    QDECREF(req);
+}
+
+/* test generated dealloc functions for generated types */
+static void test_dealloc_types(void)
+{
+    UserDefOne *ud1test, *ud1a, *ud1b;
+    UserDefOneList *ud1list;
+
+    ud1test = g_malloc0(sizeof(UserDefOne));
+    ud1test->integer = 42;
+    ud1test->string = g_strdup("hi there 42");
+
+    qapi_free_UserDefOne(ud1test);
+
+    ud1a = g_malloc0(sizeof(UserDefOne));
+    ud1a->integer = 43;
+    ud1a->string = g_strdup("hi there 43");
+
+    ud1b = g_malloc0(sizeof(UserDefOne));
+    ud1b->integer = 44;
+    ud1b->string = g_strdup("hi there 44");
+
+    ud1list = g_malloc0(sizeof(UserDefOneList));
+    ud1list->value = ud1a;
+    ud1list->next = g_malloc0(sizeof(UserDefOneList));
+    ud1list->next->value = ud1b;
+
+    qapi_free_UserDefOneList(ud1list);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/0.15/dispatch_cmd", test_dispatch_cmd);
+    g_test_add_func("/0.15/dispatch_cmd_error", test_dispatch_cmd_error);
+    g_test_add_func("/0.15/dispatch_cmd_io", test_dispatch_cmd_io);
+    g_test_add_func("/0.15/dealloc_types", test_dealloc_types);
+
+    module_call_init(MODULE_INIT_QAPI);
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
new file mode 100644
index 0000000000..f6df8cbe1e
--- /dev/null
+++ b/tests/test-qmp-input-strict.c
@@ -0,0 +1,234 @@
+/*
+ * QMP Input Visitor unit-tests (strict mode).
+ *
+ * Copyright (C) 2011-2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.com>
+ *  Paolo Bonzini <pbonzini@redhat.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 <stdarg.h>
+
+#include "qapi/qmp-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestInputVisitorData {
+    QObject *obj;
+    QmpInputVisitor *qiv;
+} TestInputVisitorData;
+
+static void validate_teardown(TestInputVisitorData *data,
+                               const void *unused)
+{
+    qobject_decref(data->obj);
+    data->obj = NULL;
+
+    if (data->qiv) {
+        qmp_input_visitor_cleanup(data->qiv);
+        data->qiv = NULL;
+    }
+}
+
+/* This is provided instead of a test setup function so that the JSON
+   string used by the tests are kept in the test functions (and not
+   int main()) */
+static GCC_FMT_ATTR(2, 3)
+Visitor *validate_test_init(TestInputVisitorData *data,
+                             const char *json_string, ...)
+{
+    Visitor *v;
+    va_list ap;
+
+    va_start(ap, json_string);
+    data->obj = qobject_from_jsonv(json_string, &ap);
+    va_end(ap);
+
+    g_assert(data->obj != NULL);
+
+    data->qiv = qmp_input_visitor_new_strict(data->obj);
+    g_assert(data->qiv != NULL);
+
+    v = qmp_input_get_visitor(data->qiv);
+    g_assert(v != NULL);
+
+    return v;
+}
+
+typedef struct TestStruct
+{
+    int64_t integer;
+    bool boolean;
+    char *string;
+} TestStruct;
+
+static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
+                                  const char *name, Error **errp)
+{
+    visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
+                       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);
+
+    visit_end_struct(v, errp);
+}
+
+static void test_validate_struct(TestInputVisitorData *data,
+                                  const void *unused)
+{
+    TestStruct *p = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
+
+    visit_type_TestStruct(v, &p, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_free(p->string);
+    g_free(p);
+}
+
+static void test_validate_struct_nested(TestInputVisitorData *data,
+                                         const void *unused)
+{
+    UserDefNested *udp = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}");
+
+    visit_type_UserDefNested(v, &udp, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    qapi_free_UserDefNested(udp);
+}
+
+static void test_validate_list(TestInputVisitorData *data,
+                                const void *unused)
+{
+    UserDefOneList *head = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");
+
+    visit_type_UserDefOneList(v, &head, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    qapi_free_UserDefOneList(head);
+}
+
+static void test_validate_union(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    UserDefUnion *tmp = NULL;
+    Visitor *v;
+    Error *errp = NULL;
+
+    v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }");
+
+    visit_type_UserDefUnion(v, &tmp, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    qapi_free_UserDefUnion(tmp);
+}
+
+static void test_validate_fail_struct(TestInputVisitorData *data,
+                                       const void *unused)
+{
+    TestStruct *p = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }");
+
+    visit_type_TestStruct(v, &p, NULL, &errp);
+    g_assert(error_is_set(&errp));
+    if (p) {
+        g_free(p->string);
+    }
+    g_free(p);
+}
+
+static void test_validate_fail_struct_nested(TestInputVisitorData *data,
+                                              const void *unused)
+{
+    UserDefNested *udp = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}");
+
+    visit_type_UserDefNested(v, &udp, NULL, &errp);
+    g_assert(error_is_set(&errp));
+    qapi_free_UserDefNested(udp);
+}
+
+static void test_validate_fail_list(TestInputVisitorData *data,
+                                     const void *unused)
+{
+    UserDefOneList *head = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]");
+
+    visit_type_UserDefOneList(v, &head, NULL, &errp);
+    g_assert(error_is_set(&errp));
+    qapi_free_UserDefOneList(head);
+}
+
+static void test_validate_fail_union(TestInputVisitorData *data,
+                                      const void *unused)
+{
+    UserDefUnion *tmp = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = validate_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 }, 'extra': 'yyy' }");
+
+    visit_type_UserDefUnion(v, &tmp, NULL, &errp);
+    g_assert(error_is_set(&errp));
+    qapi_free_UserDefUnion(tmp);
+}
+
+static void validate_test_add(const char *testpath,
+                               TestInputVisitorData *data,
+                               void (*test_func)(TestInputVisitorData *data, const void *user_data))
+{
+    g_test_add(testpath, TestInputVisitorData, data, NULL, test_func,
+               validate_teardown);
+}
+
+int main(int argc, char **argv)
+{
+    TestInputVisitorData testdata;
+
+    g_test_init(&argc, &argv, NULL);
+
+    validate_test_add("/visitor/input-strict/pass/struct",
+                       &testdata, test_validate_struct);
+    validate_test_add("/visitor/input-strict/pass/struct-nested",
+                       &testdata, test_validate_struct_nested);
+    validate_test_add("/visitor/input-strict/pass/list",
+                       &testdata, test_validate_list);
+    validate_test_add("/visitor/input-strict/pass/union",
+                       &testdata, test_validate_union);
+    validate_test_add("/visitor/input-strict/fail/struct",
+                       &testdata, test_validate_fail_struct);
+    validate_test_add("/visitor/input-strict/fail/struct-nested",
+                       &testdata, test_validate_fail_struct_nested);
+    validate_test_add("/visitor/input-strict/fail/list",
+                       &testdata, test_validate_fail_list);
+    validate_test_add("/visitor/input-strict/fail/union",
+                       &testdata, test_validate_fail_union);
+
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
new file mode 100644
index 0000000000..c30fdc4e59
--- /dev/null
+++ b/tests/test-qmp-input-visitor.c
@@ -0,0 +1,308 @@
+/*
+ * QMP Input Visitor unit-tests.
+ *
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.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 <stdarg.h>
+
+#include "qapi/qmp-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestInputVisitorData {
+    QObject *obj;
+    QmpInputVisitor *qiv;
+} TestInputVisitorData;
+
+static void visitor_input_teardown(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    qobject_decref(data->obj);
+    data->obj = NULL;
+
+    if (data->qiv) {
+        qmp_input_visitor_cleanup(data->qiv);
+        data->qiv = NULL;
+    }
+}
+
+/* This is provided instead of a test setup function so that the JSON
+   string used by the tests are kept in the test functions (and not
+   int main()) */
+static GCC_FMT_ATTR(2, 3)
+Visitor *visitor_input_test_init(TestInputVisitorData *data,
+                                 const char *json_string, ...)
+{
+    Visitor *v;
+    va_list ap;
+
+    va_start(ap, json_string);
+    data->obj = qobject_from_jsonv(json_string, &ap);
+    va_end(ap);
+
+    g_assert(data->obj != NULL);
+
+    data->qiv = qmp_input_visitor_new(data->obj);
+    g_assert(data->qiv != NULL);
+
+    v = qmp_input_get_visitor(data->qiv);
+    g_assert(v != NULL);
+
+    return v;
+}
+
+static void test_visitor_in_int(TestInputVisitorData *data,
+                                const void *unused)
+{
+    int64_t res = 0, value = -42;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "%" PRId64, value);
+
+    visit_type_int(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, value);
+}
+
+static void test_visitor_in_bool(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    Error *errp = NULL;
+    bool res = false;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "true");
+
+    visit_type_bool(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, true);
+}
+
+static void test_visitor_in_number(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    double res = 0, value = 3.14;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "%f", value);
+
+    visit_type_number(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_string(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    char *res = NULL, *value = (char *) "Q E M U";
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "%s", value);
+
+    visit_type_str(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpstr(res, ==, value);
+
+    g_free(res);
+}
+
+static void test_visitor_in_enum(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    Error *errp = NULL;
+    Visitor *v;
+    EnumOne i;
+
+    for (i = 0; EnumOne_lookup[i]; i++) {
+        EnumOne res = -1;
+
+        v = visitor_input_test_init(data, "%s", EnumOne_lookup[i]);
+
+        visit_type_EnumOne(v, &res, NULL, &errp);
+        g_assert(!error_is_set(&errp));
+        g_assert_cmpint(i, ==, res);
+
+        visitor_input_teardown(data, NULL);
+    }
+
+    data->obj = NULL;
+    data->qiv = NULL;
+}
+
+typedef struct TestStruct
+{
+    int64_t integer;
+    bool boolean;
+    char *string;
+} TestStruct;
+
+static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
+                                  const char *name, Error **errp)
+{
+    visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
+                       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);
+
+    visit_end_struct(v, errp);
+}
+
+static void test_visitor_in_struct(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    TestStruct *p = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
+
+    visit_type_TestStruct(v, &p, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(p->integer, ==, -42);
+    g_assert(p->boolean == true);
+    g_assert_cmpstr(p->string, ==, "foo");
+
+    g_free(p->string);
+    g_free(p);
+}
+
+static void check_and_free_str(char *str, const char *cmp)
+{
+    g_assert_cmpstr(str, ==, cmp);
+    g_free(str);
+}
+
+static void test_visitor_in_struct_nested(TestInputVisitorData *data,
+                                          const void *unused)
+{
+    UserDefNested *udp = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}");
+
+    visit_type_UserDefNested(v, &udp, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    check_and_free_str(udp->string0, "string0");
+    check_and_free_str(udp->dict1.string1, "string1");
+    g_assert_cmpint(udp->dict1.dict2.userdef1->integer, ==, 42);
+    check_and_free_str(udp->dict1.dict2.userdef1->string, "string");
+    check_and_free_str(udp->dict1.dict2.string2, "string2");
+    g_assert(udp->dict1.has_dict3 == false);
+
+    g_free(udp->dict1.dict2.userdef1);
+    g_free(udp);
+}
+
+static void test_visitor_in_list(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    UserDefOneList *item, *head = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+    int i;
+
+    v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");
+
+    visit_type_UserDefOneList(v, &head, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert(head != NULL);
+
+    for (i = 0, item = head; item; item = item->next, i++) {
+        char string[12];
+
+        snprintf(string, sizeof(string), "string%d", i);
+        g_assert_cmpstr(item->value->string, ==, string);
+        g_assert_cmpint(item->value->integer, ==, 42 + i);
+    }
+
+    qapi_free_UserDefOneList(head);
+}
+
+static void test_visitor_in_union(TestInputVisitorData *data,
+                                  const void *unused)
+{
+    Visitor *v;
+    Error *err = NULL;
+    UserDefUnion *tmp;
+
+    v = visitor_input_test_init(data, "{ 'type': 'b', 'data' : { 'integer': 42 } }");
+
+    visit_type_UserDefUnion(v, &tmp, NULL, &err);
+    g_assert(err == NULL);
+    g_assert_cmpint(tmp->kind, ==, USER_DEF_UNION_KIND_B);
+    g_assert_cmpint(tmp->b->integer, ==, 42);
+    qapi_free_UserDefUnion(tmp);
+}
+
+static void input_visitor_test_add(const char *testpath,
+                                   TestInputVisitorData *data,
+                                   void (*test_func)(TestInputVisitorData *data, const void *user_data))
+{
+    g_test_add(testpath, TestInputVisitorData, data, NULL, test_func,
+               visitor_input_teardown);
+}
+
+static void test_visitor_in_errors(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    TestStruct *p = NULL;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "{ 'integer': false, 'boolean': 'foo', 'string': -42 }");
+
+    visit_type_TestStruct(v, &p, NULL, &errp);
+    g_assert(error_is_set(&errp));
+    g_assert(p->string == NULL);
+
+    g_free(p->string);
+    g_free(p);
+}
+
+int main(int argc, char **argv)
+{
+    TestInputVisitorData in_visitor_data;
+
+    g_test_init(&argc, &argv, NULL);
+
+    input_visitor_test_add("/visitor/input/int",
+                           &in_visitor_data, test_visitor_in_int);
+    input_visitor_test_add("/visitor/input/bool",
+                           &in_visitor_data, test_visitor_in_bool);
+    input_visitor_test_add("/visitor/input/number",
+                           &in_visitor_data, test_visitor_in_number);
+    input_visitor_test_add("/visitor/input/string",
+                            &in_visitor_data, test_visitor_in_string);
+    input_visitor_test_add("/visitor/input/enum",
+                            &in_visitor_data, test_visitor_in_enum);
+    input_visitor_test_add("/visitor/input/struct",
+                            &in_visitor_data, test_visitor_in_struct);
+    input_visitor_test_add("/visitor/input/struct-nested",
+                            &in_visitor_data, test_visitor_in_struct_nested);
+    input_visitor_test_add("/visitor/input/list",
+                            &in_visitor_data, test_visitor_in_list);
+    input_visitor_test_add("/visitor/input/union",
+                            &in_visitor_data, test_visitor_in_union);
+    input_visitor_test_add("/visitor/input/errors",
+                            &in_visitor_data, test_visitor_in_errors);
+
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
new file mode 100644
index 0000000000..24a6359504
--- /dev/null
+++ b/tests/test-qmp-output-visitor.c
@@ -0,0 +1,477 @@
+/*
+ * QMP Output Visitor unit-tests.
+ *
+ * Copyright (C) 2011 Red Hat Inc.
+ *
+ * Authors:
+ *  Luiz Capitulino <lcapitulino@redhat.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 "qapi/qmp-output-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestOutputVisitorData {
+    QmpOutputVisitor *qov;
+    Visitor *ov;
+} TestOutputVisitorData;
+
+static void visitor_output_setup(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    data->qov = qmp_output_visitor_new();
+    g_assert(data->qov != NULL);
+
+    data->ov = qmp_output_get_visitor(data->qov);
+    g_assert(data->ov != NULL);
+}
+
+static void visitor_output_teardown(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    qmp_output_visitor_cleanup(data->qov);
+    data->qov = NULL;
+    data->ov = NULL;
+}
+
+static void test_visitor_out_int(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    int64_t value = -42;
+    Error *errp = NULL;
+    QObject *obj;
+
+    visit_type_int(data->ov, &value, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QINT);
+    g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, value);
+
+    qobject_decref(obj);
+}
+
+static void test_visitor_out_bool(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    Error *errp = NULL;
+    bool value = true;
+    QObject *obj;
+
+    visit_type_bool(data->ov, &value, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QBOOL);
+    g_assert(qbool_get_int(qobject_to_qbool(obj)) == value);
+
+    qobject_decref(obj);
+}
+
+static void test_visitor_out_number(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    double value = 3.14;
+    Error *errp = NULL;
+    QObject *obj;
+
+    visit_type_number(data->ov, &value, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QFLOAT);
+    g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value);
+
+    qobject_decref(obj);
+}
+
+static void test_visitor_out_string(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    char *string = (char *) "Q E M U";
+    Error *errp = NULL;
+    QObject *obj;
+
+    visit_type_str(data->ov, &string, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QSTRING);
+    g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string);
+
+    qobject_decref(obj);
+}
+
+static void test_visitor_out_no_string(TestOutputVisitorData *data,
+                                       const void *unused)
+{
+    char *string = NULL;
+    Error *errp = NULL;
+    QObject *obj;
+
+    /* A null string should return "" */
+    visit_type_str(data->ov, &string, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QSTRING);
+    g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, "");
+
+    qobject_decref(obj);
+}
+
+static void test_visitor_out_enum(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    Error *errp = NULL;
+    QObject *obj;
+    EnumOne i;
+
+    for (i = 0; i < ENUM_ONE_MAX; i++) {
+        visit_type_EnumOne(data->ov, &i, "unused", &errp);
+        g_assert(!error_is_set(&errp));
+
+        obj = qmp_output_get_qobject(data->qov);
+        g_assert(obj != NULL);
+        g_assert(qobject_type(obj) == QTYPE_QSTRING);
+        g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==,
+                        EnumOne_lookup[i]);
+        qobject_decref(obj);
+    }
+}
+
+static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
+                                         const void *unused)
+{
+    EnumOne i, bad_values[] = { ENUM_ONE_MAX, -1 };
+    Error *errp;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+        errp = NULL;
+        visit_type_EnumOne(data->ov, &bad_values[i], "unused", &errp);
+        g_assert(error_is_set(&errp) == true);
+        error_free(errp);
+    }
+}
+
+typedef struct TestStruct
+{
+    int64_t integer;
+    bool boolean;
+    char *string;
+} TestStruct;
+
+static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
+                                  const char *name, Error **errp)
+{
+    visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
+                       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);
+
+    visit_end_struct(v, errp);
+}
+
+static void test_visitor_out_struct(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    TestStruct test_struct = { .integer = 42,
+                               .boolean = false,
+                               .string = (char *) "foo"};
+    TestStruct *p = &test_struct;
+    Error *errp = NULL;
+    QObject *obj;
+    QDict *qdict;
+
+    visit_type_TestStruct(data->ov, &p, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QDICT);
+
+    qdict = qobject_to_qdict(obj);
+    g_assert_cmpint(qdict_size(qdict), ==, 3);
+    g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
+    g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, 0);
+    g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo");
+
+    QDECREF(qdict);
+}
+
+static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
+                                           const void *unused)
+{
+    int64_t value = 42;
+    Error *errp = NULL;
+    UserDefNested *ud2;
+    QObject *obj;
+    QDict *qdict, *dict1, *dict2, *dict3, *userdef;
+    const char *string = "user def string";
+    const char *strings[] = { "forty two", "forty three", "forty four",
+                              "forty five" };
+
+    ud2 = g_malloc0(sizeof(*ud2));
+    ud2->string0 = g_strdup(strings[0]);
+
+    ud2->dict1.string1 = g_strdup(strings[1]);
+    ud2->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
+    ud2->dict1.dict2.userdef1->string = g_strdup(string);
+    ud2->dict1.dict2.userdef1->integer = value;
+    ud2->dict1.dict2.string2 = g_strdup(strings[2]);
+
+    ud2->dict1.has_dict3 = true;
+    ud2->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne));
+    ud2->dict1.dict3.userdef2->string = g_strdup(string);
+    ud2->dict1.dict3.userdef2->integer = value;
+    ud2->dict1.dict3.string3 = g_strdup(strings[3]);
+
+    visit_type_UserDefNested(data->ov, &ud2, "unused", &errp);
+    g_assert(!error_is_set(&errp));
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QDICT);
+
+    qdict = qobject_to_qdict(obj);
+    g_assert_cmpint(qdict_size(qdict), ==, 2);
+    g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]);
+
+    dict1 = qdict_get_qdict(qdict, "dict1");
+    g_assert_cmpint(qdict_size(dict1), ==, 3);
+    g_assert_cmpstr(qdict_get_str(dict1, "string1"), ==, strings[1]);
+
+    dict2 = qdict_get_qdict(dict1, "dict2");
+    g_assert_cmpint(qdict_size(dict2), ==, 2);
+    g_assert_cmpstr(qdict_get_str(dict2, "string2"), ==, strings[2]);
+    userdef = qdict_get_qdict(dict2, "userdef1");
+    g_assert_cmpint(qdict_size(userdef), ==, 2);
+    g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
+    g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
+
+    dict3 = qdict_get_qdict(dict1, "dict3");
+    g_assert_cmpint(qdict_size(dict3), ==, 2);
+    g_assert_cmpstr(qdict_get_str(dict3, "string3"), ==, strings[3]);
+    userdef = qdict_get_qdict(dict3, "userdef2");
+    g_assert_cmpint(qdict_size(userdef), ==, 2);
+    g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
+    g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
+
+    QDECREF(qdict);
+    qapi_free_UserDefNested(ud2);
+}
+
+static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
+                                           const void *unused)
+{
+    EnumOne bad_values[] = { ENUM_ONE_MAX, -1 };
+    UserDefOne u = { 0 }, *pu = &u;
+    Error *errp;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+        errp = NULL;
+        u.has_enum1 = true;
+        u.enum1 = bad_values[i];
+        visit_type_UserDefOne(data->ov, &pu, "unused", &errp);
+        g_assert(error_is_set(&errp) == true);
+        error_free(errp);
+    }
+}
+
+typedef struct TestStructList
+{
+    TestStruct *value;
+    struct TestStructList *next;
+} TestStructList;
+
+static void visit_type_TestStructList(Visitor *v, TestStructList **obj,
+                                      const char *name, Error **errp)
+{
+    GenericList *i, **head = (GenericList **)obj;
+
+    visit_start_list(v, name, errp);
+
+    for (*head = i = visit_next_list(v, head, errp); i; i = visit_next_list(v, &i, errp)) {
+        TestStructList *native_i = (TestStructList *)i;
+        visit_type_TestStruct(v, &native_i->value, NULL, errp);
+    }
+
+    visit_end_list(v, errp);
+}
+
+static void test_visitor_out_list(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    char *value_str = (char *) "list value";
+    TestStructList *p, *head = NULL;
+    const int max_items = 10;
+    bool value_bool = true;
+    int value_int = 10;
+    Error *errp = NULL;
+    QListEntry *entry;
+    QObject *obj;
+    QList *qlist;
+    int i;
+
+    for (i = 0; i < max_items; i++) {
+        p = g_malloc0(sizeof(*p));
+        p->value = g_malloc0(sizeof(*p->value));
+        p->value->integer = value_int;
+        p->value->boolean = value_bool;
+        p->value->string = value_str;
+
+        p->next = head;
+        head = p;
+    }
+
+    visit_type_TestStructList(data->ov, &head, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+
+    obj = qmp_output_get_qobject(data->qov);
+    g_assert(obj != NULL);
+    g_assert(qobject_type(obj) == QTYPE_QLIST);
+
+    qlist = qobject_to_qlist(obj);
+    g_assert(!qlist_empty(qlist));
+
+    i = 0;
+    QLIST_FOREACH_ENTRY(qlist, entry) {
+        QDict *qdict;
+
+        g_assert(qobject_type(entry->value) == QTYPE_QDICT);
+        qdict = qobject_to_qdict(entry->value);
+        g_assert_cmpint(qdict_size(qdict), ==, 3);
+        g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int);
+        g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool);
+        g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str);
+        i++;
+    }
+    g_assert_cmpint(i, ==, max_items);
+
+    QDECREF(qlist);
+
+    for (p = head; p;) {
+        TestStructList *tmp = p->next;
+        g_free(p->value);
+        g_free(p);
+        p = tmp;
+    }
+}
+
+static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
+                                            const void *unused)
+{
+    UserDefNestedList *p, *head = NULL;
+    const char string[] = "foo bar";
+    int i, max_count = 1024;
+
+    for (i = 0; i < max_count; i++) {
+        p = g_malloc0(sizeof(*p));
+        p->value = g_malloc0(sizeof(*p->value));
+
+        p->value->string0 = g_strdup(string);
+        p->value->dict1.string1 = g_strdup(string);
+        p->value->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne));
+        p->value->dict1.dict2.userdef1->string = g_strdup(string);
+        p->value->dict1.dict2.userdef1->integer = 42;
+        p->value->dict1.dict2.string2 = g_strdup(string);
+        p->value->dict1.has_dict3 = false;
+
+        p->next = head;
+        head = p;
+    }
+
+    qapi_free_UserDefNestedList(head);
+}
+
+static void test_visitor_out_union(TestOutputVisitorData *data,
+                                   const void *unused)
+{
+    QObject *arg, *qvalue;
+    QDict *qdict, *value;
+
+    Error *err = NULL;
+
+    UserDefUnion *tmp = g_malloc0(sizeof(UserDefUnion));
+    tmp->kind = USER_DEF_UNION_KIND_A;
+    tmp->a = g_malloc0(sizeof(UserDefA));
+    tmp->a->boolean = true;
+
+    visit_type_UserDefUnion(data->ov, &tmp, NULL, &err);
+    g_assert(err == NULL);
+    arg = qmp_output_get_qobject(data->qov);
+
+    g_assert(qobject_type(arg) == QTYPE_QDICT);
+    qdict = qobject_to_qdict(arg);
+
+    g_assert_cmpstr(qdict_get_str(qdict, "type"), ==, "a");
+
+    qvalue = qdict_get(qdict, "data");
+    g_assert(data != NULL);
+    g_assert(qobject_type(qvalue) == QTYPE_QDICT);
+    value = qobject_to_qdict(qvalue);
+    g_assert_cmpint(qdict_get_bool(value, "boolean"), ==, true);
+
+    qapi_free_UserDefUnion(tmp);
+    QDECREF(qdict);
+}
+
+static void output_visitor_test_add(const char *testpath,
+                                    TestOutputVisitorData *data,
+                                    void (*test_func)(TestOutputVisitorData *data, const void *user_data))
+{
+    g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup,
+               test_func, visitor_output_teardown);
+}
+
+int main(int argc, char **argv)
+{
+    TestOutputVisitorData out_visitor_data;
+
+    g_test_init(&argc, &argv, NULL);
+
+    output_visitor_test_add("/visitor/output/int",
+                            &out_visitor_data, test_visitor_out_int);
+    output_visitor_test_add("/visitor/output/bool",
+                            &out_visitor_data, test_visitor_out_bool);
+    output_visitor_test_add("/visitor/output/number",
+                            &out_visitor_data, test_visitor_out_number);
+    output_visitor_test_add("/visitor/output/string",
+                            &out_visitor_data, test_visitor_out_string);
+    output_visitor_test_add("/visitor/output/no-string",
+                            &out_visitor_data, test_visitor_out_no_string);
+    output_visitor_test_add("/visitor/output/enum",
+                            &out_visitor_data, test_visitor_out_enum);
+    output_visitor_test_add("/visitor/output/enum-errors",
+                            &out_visitor_data, test_visitor_out_enum_errors);
+    output_visitor_test_add("/visitor/output/struct",
+                            &out_visitor_data, test_visitor_out_struct);
+    output_visitor_test_add("/visitor/output/struct-nested",
+                            &out_visitor_data, test_visitor_out_struct_nested);
+    output_visitor_test_add("/visitor/output/struct-errors",
+                            &out_visitor_data, test_visitor_out_struct_errors);
+    output_visitor_test_add("/visitor/output/list",
+                            &out_visitor_data, test_visitor_out_list);
+    output_visitor_test_add("/visitor/output/list-qapi-free",
+                            &out_visitor_data, test_visitor_out_list_qapi_free);
+    output_visitor_test_add("/visitor/output/union",
+                            &out_visitor_data, test_visitor_out_union);
+
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c
new file mode 100644
index 0000000000..5370e32041
--- /dev/null
+++ b/tests/test-string-input-visitor.c
@@ -0,0 +1,195 @@
+/*
+ * String Input Visitor unit-tests.
+ *
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Paolo Bonzini <pbonzini@redhat.com> (based on test-qmp-input-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 <glib.h>
+#include <stdarg.h>
+
+#include "qapi/string-input-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestInputVisitorData {
+    StringInputVisitor *siv;
+} TestInputVisitorData;
+
+static void visitor_input_teardown(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    if (data->siv) {
+        string_input_visitor_cleanup(data->siv);
+        data->siv = NULL;
+    }
+}
+
+/* This is provided instead of a test setup function so that the JSON
+   string used by the tests are kept in the test functions (and not
+   int main()) */
+static
+Visitor *visitor_input_test_init(TestInputVisitorData *data,
+                                 const char *string)
+{
+    Visitor *v;
+
+    data->siv = string_input_visitor_new(string);
+    g_assert(data->siv != NULL);
+
+    v = string_input_get_visitor(data->siv);
+    g_assert(v != NULL);
+
+    return v;
+}
+
+static void test_visitor_in_int(TestInputVisitorData *data,
+                                const void *unused)
+{
+    int64_t res = 0, value = -42;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "-42");
+
+    visit_type_int(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, value);
+}
+
+static void test_visitor_in_bool(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    Error *errp = NULL;
+    bool res = false;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "true");
+
+    visit_type_bool(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, true);
+    visitor_input_teardown(data, unused);
+
+    v = visitor_input_test_init(data, "yes");
+
+    visit_type_bool(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, true);
+    visitor_input_teardown(data, unused);
+
+    v = visitor_input_test_init(data, "on");
+
+    visit_type_bool(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, true);
+    visitor_input_teardown(data, unused);
+
+    v = visitor_input_test_init(data, "false");
+
+    visit_type_bool(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, false);
+    visitor_input_teardown(data, unused);
+
+    v = visitor_input_test_init(data, "no");
+
+    visit_type_bool(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, false);
+    visitor_input_teardown(data, unused);
+
+    v = visitor_input_test_init(data, "off");
+
+    visit_type_bool(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpint(res, ==, false);
+}
+
+static void test_visitor_in_number(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    double res = 0, value = 3.14;
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, "3.14");
+
+    visit_type_number(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpfloat(res, ==, value);
+}
+
+static void test_visitor_in_string(TestInputVisitorData *data,
+                                   const void *unused)
+{
+    char *res = NULL, *value = (char *) "Q E M U";
+    Error *errp = NULL;
+    Visitor *v;
+
+    v = visitor_input_test_init(data, value);
+
+    visit_type_str(v, &res, NULL, &errp);
+    g_assert(!error_is_set(&errp));
+    g_assert_cmpstr(res, ==, value);
+
+    g_free(res);
+}
+
+static void test_visitor_in_enum(TestInputVisitorData *data,
+                                 const void *unused)
+{
+    Error *errp = NULL;
+    Visitor *v;
+    EnumOne i;
+
+    for (i = 0; EnumOne_lookup[i]; i++) {
+        EnumOne res = -1;
+
+        v = visitor_input_test_init(data, EnumOne_lookup[i]);
+
+        visit_type_EnumOne(v, &res, NULL, &errp);
+        g_assert(!error_is_set(&errp));
+        g_assert_cmpint(i, ==, res);
+
+        visitor_input_teardown(data, NULL);
+    }
+
+    data->siv = NULL;
+}
+
+static void input_visitor_test_add(const char *testpath,
+                                   TestInputVisitorData *data,
+                                   void (*test_func)(TestInputVisitorData *data, const void *user_data))
+{
+    g_test_add(testpath, TestInputVisitorData, data, NULL, test_func,
+               visitor_input_teardown);
+}
+
+int main(int argc, char **argv)
+{
+    TestInputVisitorData in_visitor_data;
+
+    g_test_init(&argc, &argv, NULL);
+
+    input_visitor_test_add("/string-visitor/input/int",
+                           &in_visitor_data, test_visitor_in_int);
+    input_visitor_test_add("/string-visitor/input/bool",
+                           &in_visitor_data, test_visitor_in_bool);
+    input_visitor_test_add("/string-visitor/input/number",
+                           &in_visitor_data, test_visitor_in_number);
+    input_visitor_test_add("/string-visitor/input/string",
+                            &in_visitor_data, test_visitor_in_string);
+    input_visitor_test_add("/string-visitor/input/enum",
+                            &in_visitor_data, test_visitor_in_enum);
+
+    g_test_run();
+
+    return 0;
+}
diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
new file mode 100644
index 0000000000..22909b84ef
--- /dev/null
+++ b/tests/test-string-output-visitor.c
@@ -0,0 +1,188 @@
+/*
+ * String Output Visitor unit-tests.
+ *
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * Authors:
+ *  Paolo Bonzini <pbonzini@redhat.com> (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 <glib.h>
+
+#include "qapi/string-output-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qemu-objects.h"
+
+typedef struct TestOutputVisitorData {
+    StringOutputVisitor *sov;
+    Visitor *ov;
+} TestOutputVisitorData;
+
+static void visitor_output_setup(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    data->sov = string_output_visitor_new();
+    g_assert(data->sov != NULL);
+
+    data->ov = string_output_get_visitor(data->sov);
+    g_assert(data->ov != NULL);
+}
+
+static void visitor_output_teardown(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    string_output_visitor_cleanup(data->sov);
+    data->sov = NULL;
+    data->ov = NULL;
+}
+
+static void test_visitor_out_int(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    int64_t value = -42;
+    Error *errp = NULL;
+    char *str;
+
+    visit_type_int(data->ov, &value, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    str = string_output_get_string(data->sov);
+    g_assert(str != NULL);
+    g_assert_cmpstr(str, ==, "-42");
+    g_free(str);
+}
+
+static void test_visitor_out_bool(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    Error *errp = NULL;
+    bool value = true;
+    char *str;
+
+    visit_type_bool(data->ov, &value, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    str = string_output_get_string(data->sov);
+    g_assert(str != NULL);
+    g_assert_cmpstr(str, ==, "true");
+    g_free(str);
+}
+
+static void test_visitor_out_number(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    double value = 3.14;
+    Error *errp = NULL;
+    char *str;
+
+    visit_type_number(data->ov, &value, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    str = string_output_get_string(data->sov);
+    g_assert(str != NULL);
+    g_assert_cmpstr(str, ==, "3.14");
+    g_free(str);
+}
+
+static void test_visitor_out_string(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    char *string = (char *) "Q E M U";
+    Error *errp = NULL;
+    char *str;
+
+    visit_type_str(data->ov, &string, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    str = string_output_get_string(data->sov);
+    g_assert(str != NULL);
+    g_assert_cmpstr(str, ==, string);
+    g_free(str);
+}
+
+static void test_visitor_out_no_string(TestOutputVisitorData *data,
+                                       const void *unused)
+{
+    char *string = NULL;
+    Error *errp = NULL;
+    char *str;
+
+    /* A null string should return "" */
+    visit_type_str(data->ov, &string, NULL, &errp);
+    g_assert(error_is_set(&errp) == 0);
+
+    str = string_output_get_string(data->sov);
+    g_assert(str != NULL);
+    g_assert_cmpstr(str, ==, "");
+    g_free(str);
+}
+
+static void test_visitor_out_enum(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    Error *errp = NULL;
+    char *str;
+    EnumOne i;
+
+    for (i = 0; i < ENUM_ONE_MAX; i++) {
+        visit_type_EnumOne(data->ov, &i, "unused", &errp);
+        g_assert(!error_is_set(&errp));
+
+        str = string_output_get_string(data->sov);
+        g_assert(str != NULL);
+        g_assert_cmpstr(str, ==, EnumOne_lookup[i]);
+	g_free(str);
+    }
+}
+
+static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
+                                         const void *unused)
+{
+    EnumOne i, bad_values[] = { ENUM_ONE_MAX, -1 };
+    Error *errp;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+        errp = NULL;
+        visit_type_EnumOne(data->ov, &bad_values[i], "unused", &errp);
+        g_assert(error_is_set(&errp) == true);
+        error_free(errp);
+    }
+}
+
+static void output_visitor_test_add(const char *testpath,
+                                    TestOutputVisitorData *data,
+                                    void (*test_func)(TestOutputVisitorData *data, const void *user_data))
+{
+    g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup,
+               test_func, visitor_output_teardown);
+}
+
+int main(int argc, char **argv)
+{
+    TestOutputVisitorData out_visitor_data;
+
+    g_test_init(&argc, &argv, NULL);
+
+    output_visitor_test_add("/string-visitor/output/int",
+                            &out_visitor_data, test_visitor_out_int);
+    output_visitor_test_add("/string-visitor/output/bool",
+                            &out_visitor_data, test_visitor_out_bool);
+    output_visitor_test_add("/string-visitor/output/number",
+                            &out_visitor_data, test_visitor_out_number);
+    output_visitor_test_add("/string-visitor/output/string",
+                            &out_visitor_data, test_visitor_out_string);
+    output_visitor_test_add("/string-visitor/output/no-string",
+                            &out_visitor_data, test_visitor_out_no_string);
+    output_visitor_test_add("/string-visitor/output/enum",
+                            &out_visitor_data, test_visitor_out_enum);
+    output_visitor_test_add("/string-visitor/output/enum-errors",
+                            &out_visitor_data, test_visitor_out_enum_errors);
+
+    g_test_run();
+
+    return 0;
+}