Signed-off-by: Michael Roth <mdr...@linux.vnet.ibm.com> --- Makefile | 2 + configure | 1 + rules.mak | 25 ++- tests/Makefile | 8 +- tests/test-qidl-included.h | 31 ++++ tests/test-qidl-linked.c | 93 +++++++++++ tests/test-qidl-linked.h | 18 +++ tests/test-qidl.c | 376 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 551 insertions(+), 3 deletions(-) create mode 100644 tests/test-qidl-included.h create mode 100644 tests/test-qidl-linked.c create mode 100644 tests/test-qidl-linked.h create mode 100644 tests/test-qidl.c
diff --git a/Makefile b/Makefile index da4360f..d3405e0 100644 --- a/Makefile +++ b/Makefile @@ -236,6 +236,7 @@ clean: if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ rm -f $$d/qemu-options.def; \ done + find -depth -name qidl-generated -type d -exec rm -rf {} \; VERSION ?= $(shell cat VERSION) @@ -405,6 +406,7 @@ qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \ # rebuilt before other object files Makefile: $(GENERATED_HEADERS) + # Include automatically generated dependency files # Dependencies in Makefile.objs files come from our recursive subdir rules -include $(wildcard *.d tests/*.d) diff --git a/configure b/configure index 77b2462..39e34e8 100755 --- a/configure +++ b/configure @@ -3235,6 +3235,7 @@ if test "$debug_tcg" = "yes" ; then fi if test "$debug" = "yes" ; then echo "CONFIG_DEBUG_EXEC=y" >> $config_host_mak + echo "CONFIG_DEBUG_QIDL=y" >> $config_host_mak fi if test "$strip_opt" = "yes" ; then echo "STRIP=${strip}" >> $config_host_mak diff --git a/rules.mak b/rules.mak index 1b173aa..a1dd73c 100644 --- a/rules.mak +++ b/rules.mak @@ -14,8 +14,31 @@ MAKEFLAGS += -rR # Flags for dependency generation QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d +# Debug options for QIDL +ifdef CONFIG_DEBUG_QIDL +QIDL_FLAGS = --schema-filepath=$(*D)/qidl-generated/$(*F).qidl.schema +endif + %.o: %.c - $(call quiet-command,$(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") + +%.qidl.c: %.c $(SRC_PATH)/qidl.h $(addprefix $(SRC_PATH)/scripts/,lexer.py qidl.py qidl_parser.py qapi.py qapi_visit.py) + $(call rm -f $(*D)/qidl-generated/$(*F).qidl.*) + $(if $(strip $(shell grep "QIDL_ENABLE()" $< 1>/dev/null && echo "true")), \ + $(call quiet-command, \ + $(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(CFLAGS) -E -c -DQIDL_GEN $< | \ + $(PYTHON) $(SRC_PATH)/scripts/qidl.py $(QIDL_FLAGS) \ + --output-filepath=$(*D)/qidl-generated/$(*F).qidl.c || [ "$$?" -eq 2 ], \ + "qidl PP $(*D)/$(*F).c"),) + +%.o: %.c %.qidl.c + $(if $(strip $(shell test -f $(*D)/qidl-generated/$(*F).qidl.c && echo "true")), \ + $(call quiet-command, \ + $(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c \ + -DQIDL_ENABLED -include $< -o $@ $(*D)/qidl-generated/$(*F).qidl.c, \ + "qidl CC $@"), \ + $(call quiet-command, \ + $(CC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c \ + -o $@ $<," CC $@")) ifeq ($(LIBTOOL),) %.lo: %.c diff --git a/tests/Makefile b/tests/Makefile index e10aaed..fe2d025 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -15,6 +15,7 @@ check-unit-y += tests/test-string-output-visitor$(EXESUF) check-unit-y += tests/test-coroutine$(EXESUF) check-unit-y += tests/test-visitor-serialization$(EXESUF) check-unit-y += tests/test-iov$(EXESUF) +check-unit-y += tests/test-qidl$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh @@ -34,11 +35,12 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ - tests/test-qmp-commands.o tests/test-visitor-serialization.o + tests/test-qmp-commands.o tests/test-visitor-serialization.o \ + tests/test-qidl.o test-qapi-obj-y = $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) test-qapi-obj-y += tests/test-qapi-visit.o tests/test-qapi-types.o -test-qapi-obj-y += module.o +test-qapi-obj-y += module.o $(qom-obj-y) $(test-obj-y): QEMU_INCLUDES += -Itests @@ -84,6 +86,8 @@ check-qtest-$(CONFIG_POSIX)=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET) qtest-obj-y = tests/libqtest.o $(oslib-obj-y) $(tools-obj-y) $(check-qtest-y): $(qtest-obj-y) +tests/test-qidl$(EXESUF): tests/test-qidl.o tests/test-qidl-linked.o $(test-qapi-obj-y) qapi/misc-qapi-visit.o + .PHONY: check-help check-help: @echo "Regression testing targets:" diff --git a/tests/test-qidl-included.h b/tests/test-qidl-included.h new file mode 100644 index 0000000..535a153 --- /dev/null +++ b/tests/test-qidl-included.h @@ -0,0 +1,31 @@ +/* + * Unit-tests for QIDL-generated visitors/code + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TEST_QIDL_INCLUDED_H +#define TEST_QIDL_INCLUDED_H + +#include "qidl.h" + +typedef struct TestStructIncluded TestStructIncluded; + +QIDL_DECLARE(TestStructIncluded) { + int32_t q_immutable a; + int32_t b; + uint32_t q_immutable c; + uint32_t d; + uint64_t q_immutable e; + uint64_t q_property("f", 42) f; + char q_property("g") *g; + char q_property("h") q_immutable *h; +}; + +#endif diff --git a/tests/test-qidl-linked.c b/tests/test-qidl-linked.c new file mode 100644 index 0000000..641e561 --- /dev/null +++ b/tests/test-qidl-linked.c @@ -0,0 +1,93 @@ +/* + * Unit-tests for QIDL-generated visitors/code + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qidl.h" +#include "test-qidl-linked.h" +#include "hw/qdev-properties.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qapi-dealloc-visitor.h" + +QIDL_ENABLE() + +typedef struct TestStructLinked TestStructLinked; + +QIDL_DECLARE(TestStructLinked) { + int32_t q_immutable a; + int32_t b; + uint32_t q_immutable c; + uint32_t d; + uint64_t q_immutable e; + uint64_t q_property("f", 42) f; + char q_property("g") *g; + char q_property("h") q_immutable *h; +}; + +/* exercise generated code from annotations in objects we link against */ +void test_linked_object_annotations(gconstpointer opaque) +{ + TestStructLinked *s1, *s2 = NULL; + Property *props; + QmpInputVisitor *qiv; + QmpOutputVisitor *qov; + QObject *s1_obj; + Error *err = NULL; + + s1 = g_malloc0(sizeof(TestStructLinked)); + s1->a = 42; + s1->b = INT32_MAX; + s1->c = 43; + s1->d = UINT32_MAX; + s1->e = 44; + s1->f = UINT64_MAX; + s1->g = g_strdup("test string g"); + s1->h = g_strdup("test string h"); + + qov = qmp_output_visitor_new(); + QIDL_VISIT_TYPE(TestStructLinked, qmp_output_get_visitor(qov), &s1, NULL, &err); + g_assert(err == NULL); + + s1_obj = qmp_output_get_qobject(qov); + qiv = qmp_input_visitor_new(s1_obj); + + qobject_decref(s1_obj); + qmp_output_visitor_cleanup(qov); + g_free(s1->g); + g_free(s1->h); + g_free(s1); + + s2 = g_malloc0(sizeof(TestStructLinked)); + QIDL_VISIT_TYPE(TestStructLinked, qmp_input_get_visitor(qiv), &s2, NULL, &err); + g_assert(err == NULL); + + g_assert_cmpint(s2->a, ==, 0); + g_assert_cmpint(s2->b, ==, INT32_MAX); + g_assert_cmpint(s2->c, ==, 0); + g_assert_cmpint(s2->d, ==, UINT32_MAX); + g_assert_cmpint(s2->e, ==, 0); + g_assert_cmpint(s2->f, ==, UINT64_MAX); + g_assert_cmpstr(s2->g, ==, "test string g"); + g_assert(s2->h == NULL); + + qmp_input_visitor_cleanup(qiv); + g_free(s2->g); + g_free(s2); + + props = QIDL_PROPERTIES(TestStructLinked); + g_assert_cmpstr(props[0].name, ==, "f"); + g_assert_cmpint(props[0].defval, ==, 42); + g_assert_cmpstr(props[1].name, ==, "g"); + g_assert_cmpint(props[1].defval, ==, 0); + g_assert_cmpstr(props[2].name, ==, "h"); + g_assert_cmpint(props[2].defval, ==, 0); + g_assert(props[3].name == NULL); +} diff --git a/tests/test-qidl-linked.h b/tests/test-qidl-linked.h new file mode 100644 index 0000000..1b100a2 --- /dev/null +++ b/tests/test-qidl-linked.h @@ -0,0 +1,18 @@ +/* + * Unit-tests for QIDL-generated visitors/code + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TEST_QIDL_LINKED_H +#define TEST_QIDL_LINKED_H + +void test_linked_object_annotations(gconstpointer opaque); + +#endif diff --git a/tests/test-qidl.c b/tests/test-qidl.c new file mode 100644 index 0000000..f2616c4 --- /dev/null +++ b/tests/test-qidl.c @@ -0,0 +1,376 @@ +/* + * Unit-tests for QIDL-generated visitors/code + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <stdlib.h> +#include <stdint.h> +#include "qidl.h" +#include "test-qidl-included.h" +#include "test-qidl-linked.h" +#include "hw/qdev-properties.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qapi-dealloc-visitor.h" + +QIDL_ENABLE() + +PropertyInfo qdev_prop_uint64; +PropertyInfo qdev_prop_string; + +typedef struct TestStructMain TestStructMain; + +QIDL_DECLARE(TestStructMain) { + int32_t q_immutable a; + int32_t b; + uint32_t q_immutable c; + uint32_t d; + uint64_t q_immutable e; + uint64_t q_property("f", 42) f; + char q_property("g") *g; + char q_property("h") q_immutable *h; +}; + +static void fill_test_struct_main(TestStructMain *s) +{ + s->a = 42; + s->b = INT32_MAX; + s->c = 43; + s->d = UINT32_MAX; + s->e = 44; + s->f = UINT64_MAX; + s->g = g_strdup("test string g"); + s->h = g_strdup("test string h"); +} + +static void check_test_struct_main(TestStructMain *s) +{ + g_assert_cmpint(s->a, ==, 0); + g_assert_cmpint(s->b, ==, INT32_MAX); + g_assert_cmpint(s->c, ==, 0); + g_assert_cmpint(s->d, ==, UINT32_MAX); + g_assert_cmpint(s->e, ==, 0); + g_assert_cmpint(s->f, ==, UINT64_MAX); + g_assert_cmpstr(s->g, ==, "test string g"); + g_assert(s->h == NULL); +} + +static void free_test_struct_main(TestStructMain *s) +{ + g_free(s->g); + g_free(s->h); + g_free(s); +} + +static void check_test_struct_main_properties(void) +{ + Property *props = QIDL_PROPERTIES(TestStructMain); + + g_assert_cmpstr(props[0].name, ==, "f"); + g_assert_cmpint(props[0].defval, ==, 42); + g_assert_cmpstr(props[1].name, ==, "g"); + g_assert_cmpint(props[1].defval, ==, 0); + g_assert_cmpstr(props[2].name, ==, "h"); + g_assert_cmpint(props[2].defval, ==, 0); + g_assert(props[3].name == NULL); +} + +/* exercise generated code from annotations in main() object file */ +static void test_main_object_annotations(gconstpointer opaque) +{ + TestStructMain *s1, *s2 = NULL; + QmpInputVisitor *qiv; + QmpOutputVisitor *qov; + QObject *s1_obj; + Error *err = NULL; + + s1 = g_malloc0(sizeof(TestStructMain)); + fill_test_struct_main(s1); + + qov = qmp_output_visitor_new(); + QIDL_VISIT_TYPE(TestStructMain, qmp_output_get_visitor(qov), &s1, NULL, &err); + g_assert(err == NULL); + + s1_obj = qmp_output_get_qobject(qov); + qiv = qmp_input_visitor_new(s1_obj); + + qobject_decref(s1_obj); + qmp_output_visitor_cleanup(qov); + free_test_struct_main(s1); + + s2 = g_malloc0(sizeof(TestStructMain)); + QIDL_VISIT_TYPE(TestStructMain, qmp_input_get_visitor(qiv), &s2, NULL, &err); + g_assert(err == NULL); + check_test_struct_main(s2); + + qmp_input_visitor_cleanup(qiv); + free_test_struct_main(s2); + + check_test_struct_main_properties(); +} + +static void fill_test_struct_included(TestStructIncluded *s) +{ + fill_test_struct_main((TestStructMain *)s); +} + +static void check_test_struct_included(TestStructIncluded *s) +{ + check_test_struct_main((TestStructMain *)s); +} + +static void free_test_struct_included(TestStructIncluded *s) +{ + free_test_struct_main((TestStructMain *)s); +} + +static void check_test_struct_included_properties(void) +{ + Property *props = QIDL_PROPERTIES(TestStructIncluded); + + g_assert_cmpstr(props[0].name, ==, "f"); + g_assert_cmpint(props[0].defval, ==, 42); + g_assert_cmpstr(props[1].name, ==, "g"); + g_assert_cmpint(props[1].defval, ==, 0); + g_assert_cmpstr(props[2].name, ==, "h"); + g_assert_cmpint(props[2].defval, ==, 0); + g_assert(props[3].name == NULL); +} + +/* exercise generated code from annotations in included header files */ +static void test_header_file_annotations(gconstpointer opaque) +{ + TestStructIncluded *s1, *s2 = NULL; + QmpInputVisitor *qiv; + QmpOutputVisitor *qov; + QObject *s1_obj; + Error *err = NULL; + + s1 = g_malloc0(sizeof(TestStructIncluded)); + fill_test_struct_included(s1); + + qov = qmp_output_visitor_new(); + QIDL_VISIT_TYPE(TestStructIncluded, qmp_output_get_visitor(qov), &s1, NULL, &err); + g_assert(err == NULL); + + s1_obj = qmp_output_get_qobject(qov); + qiv = qmp_input_visitor_new(s1_obj); + + qobject_decref(s1_obj); + qmp_output_visitor_cleanup(qov); + free_test_struct_included(s1); + + s2 = g_malloc0(sizeof(TestStructIncluded)); + QIDL_VISIT_TYPE(TestStructIncluded, qmp_input_get_visitor(qiv), &s2, NULL, &err); + g_assert(err == NULL); + check_test_struct_included(s2); + + qmp_input_visitor_cleanup(qiv); + free_test_struct_included(s2); + + check_test_struct_included_properties(); +} + +typedef struct TestStructComplex TestStructComplex; + +QIDL_DECLARE(TestStructComplex) { + int8_t q_size(2) array1[4]; + size_t array2_count; + int32_t q_size(array2_count) array2[8]; + int16_t q_size((2*3)) array3[16]; + bool has_optional_array; + int32_t optional_array_count; + int8_t q_optional q_size(optional_array_count) optional_array[16]; + TestStructMain struct_array[32]; + int64_t struct_array2_count; + TestStructMain q_size(struct_array2_count) struct_array2[32]; +}; + +static void fill_test_struct_complex(TestStructComplex *s) +{ + int i; + + for (i = 0; i < 4; i++) { + s->array1[i] = i*2; + } + + s->array2_count = 6; + for (i = 0; i < s->array2_count; i++) { + s->array2[i] = i*2; + } + + for (i = 0; i < 6; i++) { + s->array3[i] = i*2; + } + + s->has_optional_array = true; + s->optional_array_count = 15; + for (i = 0; i < s->optional_array_count; i++) { + s->optional_array[i] = i*2; + } + + for (i = 0; i < 32; i++) { + fill_test_struct_main(&s->struct_array[i]); + } + + s->struct_array2_count = 31; + for (i = 0; i < s->struct_array2_count; i++) { + fill_test_struct_main(&s->struct_array2[i]); + } +} + +static void check_test_struct_complex(TestStructComplex *s) +{ + int i; + + for (i = 0; i < 4; i++) { + if (i < 2) { + g_assert_cmpint(s->array1[i], ==, i*2); + } else { + g_assert_cmpint(s->array1[i], ==, 0); + } + } + + g_assert_cmpint(s->array2_count, ==, 6); + for (i = 0; i < sizeof(s->array2)/sizeof(int32_t); i++) { + if (i < s->array2_count) { + g_assert_cmpint(s->array2[i], ==, i*2); + } else { + g_assert_cmpint(s->array2[i], ==, 0); + } + } + + g_assert(s->has_optional_array); + g_assert_cmpint(s->optional_array_count, ==, 15); + for (i = 0; i < sizeof(s->optional_array)/sizeof(int16_t); i++) { + if (i < s->optional_array_count) { + g_assert_cmpint(s->optional_array[i], ==, i*2); + } else { + g_assert_cmpint(s->optional_array[i], ==, 0); + } + } + + for (i = 0; i < 32; i++) { + check_test_struct_main(&s->struct_array[i]); + } + + g_assert_cmpint(s->struct_array2_count, ==, 31); + for (i = 0; i < sizeof(s->struct_array2)/sizeof(TestStructMain); i++) { + if (i < s->struct_array2_count) { + check_test_struct_main(&s->struct_array2[i]); + } else { + int j; + uint8_t *ptr = (uint8_t *)&s->struct_array2[i]; + for (j = 0; j < sizeof(TestStructMain); j++) { + g_assert_cmpint(ptr[0], ==, 0); + } + } + } +} + +static void test_array_annotations(gconstpointer opaque) +{ + TestStructComplex *s1, *s2 = NULL; + QmpInputVisitor *qiv; + QmpOutputVisitor *qov; + QObject *s1_obj; + Error *err = NULL; + + s1 = g_malloc0(sizeof(TestStructComplex)); + fill_test_struct_complex(s1); + + qov = qmp_output_visitor_new(); + QIDL_VISIT_TYPE(TestStructComplex, qmp_output_get_visitor(qov), &s1, NULL, &err); + g_assert(err == NULL); + + s1_obj = qmp_output_get_qobject(qov); + qiv = qmp_input_visitor_new(s1_obj); + + qobject_decref(s1_obj); + qmp_output_visitor_cleanup(qov); + g_free(s1); + + s2 = g_malloc0(sizeof(TestStructComplex)); + QIDL_VISIT_TYPE(TestStructComplex, qmp_input_get_visitor(qiv), &s2, NULL, &err); + g_assert(err == NULL); + check_test_struct_complex(s2); + + qmp_input_visitor_cleanup(qiv); + g_free(s2); +} + +typedef struct TestStructComplex2 TestStructComplex2; + +QIDL_DECLARE(TestStructComplex2) { + bool has_struct1; + TestStructMain q_optional *struct1; + TestStructMain embedded_struct1; +}; + +static void test_complex_annotations(gconstpointer opaque) +{ + TestStructComplex2 *s1, *s2 = NULL; + QmpInputVisitor *qiv; + QmpOutputVisitor *qov; + QObject *s1_obj; + Error *err = NULL; + + s1 = g_malloc0(sizeof(TestStructComplex)); + s1->has_struct1 = true; + s1->struct1 = g_malloc0(sizeof(TestStructMain)); + fill_test_struct_main(s1->struct1); + fill_test_struct_main(&s1->embedded_struct1); + + qov = qmp_output_visitor_new(); + QIDL_VISIT_TYPE(TestStructComplex2, qmp_output_get_visitor(qov), &s1, NULL, &err); + g_assert(err == NULL); + + s1_obj = qmp_output_get_qobject(qov); + qiv = qmp_input_visitor_new(s1_obj); + + qobject_decref(s1_obj); + qmp_output_visitor_cleanup(qov); + free_test_struct_main(s1->struct1); + g_free(s1); + + s2 = g_malloc0(sizeof(TestStructComplex2)); + QIDL_VISIT_TYPE(TestStructComplex2, qmp_input_get_visitor(qiv), &s2, NULL, &err); + g_assert(err == NULL); + check_test_struct_main(s2->struct1); + check_test_struct_main(&s2->embedded_struct1); + + qmp_input_visitor_cleanup(qiv); + free_test_struct_main(s2->struct1); + g_free(s2); +} + +int main(int argc, char **argv) +{ + module_call_init(MODULE_INIT_QOM); + module_call_init(MODULE_INIT_QIDL); + + g_test_init(&argc, &argv, NULL); + + g_test_add_data_func("/qidl/build_test/main_object_annotations", NULL, + test_main_object_annotations); + g_test_add_data_func("/qidl/build_test/linked_object_annotations", NULL, + test_linked_object_annotations); + g_test_add_data_func("/qidl/build_test/header_file_annotations", NULL, + test_header_file_annotations); + g_test_add_data_func("/qidl/build_test/array_annotations", NULL, + test_array_annotations); + g_test_add_data_func("/qidl/build_test/complex_annotations", NULL, + test_complex_annotations); + + g_test_run(); + + return 0; +} -- 1.7.9.5