summaryrefslogtreecommitdiffstats
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/tcg/lm32/Makefile13
-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
18 files changed, 4358 insertions, 47 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/tcg/lm32/Makefile b/tests/tcg/lm32/Makefile
index 03a1abbcfb..9a00ef7ea9 100644
--- a/tests/tcg/lm32/Makefile
+++ b/tests/tcg/lm32/Makefile
@@ -1,4 +1,4 @@
--include ../../config-host.mak
+-include ../../../config-host.mak
CROSS=lm32-elf-
@@ -12,7 +12,10 @@ SIZE = $(CROSS)size
LD = $(CC)
OBJCOPY = $(CROSS)objcopy
-LDFLAGS = -Tlinker.ld
+TSRC_PATH = $(SRC_PATH)/tests/tcg/lm32
+
+LDFLAGS = -T$(TSRC_PATH)/linker.ld
+ASFLAGS += -Wa,-I,$(TSRC_PATH)/
CRT = crt.o
TESTCASES += test_add.tst
@@ -82,13 +85,13 @@ TESTCASES += test_xori.tst
all: build
-%.o: $(SRC_PATH)/tests/lm32/%.c
+%.o: $(TSRC_PATH)/%.c
$(CC) $(CFLAGS) -c $< -o $@
-%.o: $(SRC_PATH)/tests/lm32/%.S
+%.o: $(TSRC_PATH)/%.S
$(AS) $(ASFLAGS) -c $< -o $@
-%.tst: %.o macros.inc $(CRT)
+%.tst: %.o $(TSRC_PATH)/macros.inc $(CRT)
$(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@
build: $(CRT) $(TESTCASES)
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;
+}