diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile | 167 | ||||
-rw-r--r-- | tests/check-qdict.c | 378 | ||||
-rw-r--r-- | tests/check-qfloat.c | 53 | ||||
-rw-r--r-- | tests/check-qint.c | 87 | ||||
-rw-r--r-- | tests/check-qjson.c | 728 | ||||
-rw-r--r-- | tests/check-qlist.c | 127 | ||||
-rw-r--r-- | tests/check-qstring.c | 107 | ||||
-rw-r--r-- | tests/libqtest.c | 387 | ||||
-rw-r--r-- | tests/libqtest.h | 335 | ||||
-rw-r--r-- | tests/rtc-test.c | 263 | ||||
-rw-r--r-- | tests/tcg/lm32/Makefile | 13 | ||||
-rw-r--r-- | tests/test-coroutine.c | 219 | ||||
-rw-r--r-- | tests/test-qmp-commands.c | 139 | ||||
-rw-r--r-- | tests/test-qmp-input-strict.c | 234 | ||||
-rw-r--r-- | tests/test-qmp-input-visitor.c | 308 | ||||
-rw-r--r-- | tests/test-qmp-output-visitor.c | 477 | ||||
-rw-r--r-- | tests/test-string-input-visitor.c | 195 | ||||
-rw-r--r-- | tests/test-string-output-visitor.c | 188 |
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; +} |