aboutsummaryrefslogtreecommitdiff
path: root/tests/unit
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit')
-rw-r--r--tests/unit/meson.build1
-rw-r--r--tests/unit/test-forward-visitor.c197
2 files changed, 198 insertions, 0 deletions
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 3e0504dd21..5736d285b2 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -14,6 +14,7 @@ tests = {
'test-qobject-output-visitor': [testqapi],
'test-clone-visitor': [testqapi],
'test-qobject-input-visitor': [testqapi],
+ 'test-forward-visitor': [testqapi],
'test-string-input-visitor': [testqapi],
'test-string-output-visitor': [testqapi],
'test-opts-visitor': [testqapi],
diff --git a/tests/unit/test-forward-visitor.c b/tests/unit/test-forward-visitor.c
new file mode 100644
index 0000000000..348f7e4e81
--- /dev/null
+++ b/tests/unit/test-forward-visitor.c
@@ -0,0 +1,197 @@
+/*
+ * QAPI Forwarding Visitor unit-tests.
+ *
+ * Copyright (C) 2021 Red Hat Inc.
+ *
+ * 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 "qemu/osdep.h"
+
+#include "qemu-common.h"
+#include "qapi/forward-visitor.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qobject.h"
+#include "qapi/qmp/qdict.h"
+#include "test-qapi-visit.h"
+#include "qemu/option.h"
+
+typedef bool GenericVisitor (Visitor *, const char *, void **, Error **);
+#define CAST_VISIT_TYPE(fn) ((GenericVisitor *)(fn))
+
+/*
+ * Parse @srcstr and wrap it with a ForwardFieldVisitor converting "src" to
+ * "dst". Check that visiting the result with "src" name fails, and return
+ * the result of visiting "dst".
+ */
+static void *visit_with_forward(const char *srcstr, GenericVisitor *fn)
+{
+ bool help = false;
+ QDict *src = keyval_parse(srcstr, NULL, &help, &error_abort);
+ Visitor *v, *alias_v;
+ Error *err = NULL;
+ void *result = NULL;
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(src));
+ visit_start_struct(v, NULL, NULL, 0, &error_abort);
+
+ alias_v = visitor_forward_field(v, "dst", "src");
+ fn(alias_v, "src", &result, &err);
+ error_free_or_abort(&err);
+ assert(!result);
+ fn(alias_v, "dst", &result, &err);
+ assert(err == NULL);
+ visit_free(alias_v);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ qobject_unref(QOBJECT(src));
+ return result;
+}
+
+static void test_forward_any(void)
+{
+ QObject *src = visit_with_forward("src.integer=42,src.string=Hello,src.enum1=value2",
+ CAST_VISIT_TYPE(visit_type_any));
+ Visitor *v = qobject_input_visitor_new_keyval(src);
+ Error *err = NULL;
+ UserDefOne *dst;
+
+ visit_type_UserDefOne(v, NULL, &dst, &err);
+ assert(err == NULL);
+ visit_free(v);
+
+ g_assert_cmpint(dst->integer, ==, 42);
+ g_assert_cmpstr(dst->string, ==, "Hello");
+ g_assert_cmpint(dst->has_enum1, ==, true);
+ g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2);
+ qapi_free_UserDefOne(dst);
+ qobject_unref(QOBJECT(src));
+}
+
+static void test_forward_size(void)
+{
+ /*
+ * visit_type_size does not return a pointer, so visit_with_forward
+ * cannot be used.
+ */
+ bool help = false;
+ QDict *src = keyval_parse("src=1.5M", NULL, &help, &error_abort);
+ Visitor *v, *alias_v;
+ Error *err = NULL;
+ uint64_t result = 0;
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(src));
+ visit_start_struct(v, NULL, NULL, 0, &error_abort);
+
+ alias_v = visitor_forward_field(v, "dst", "src");
+ visit_type_size(alias_v, "src", &result, &err);
+ error_free_or_abort(&err);
+ visit_type_size(alias_v, "dst", &result, &err);
+ assert(result == 3 << 19);
+ assert(err == NULL);
+ visit_free(alias_v);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ qobject_unref(QOBJECT(src));
+}
+
+static void test_forward_number(void)
+{
+ /*
+ * visit_type_number does not return a pointer, so visit_with_forward
+ * cannot be used.
+ */
+ bool help = false;
+ QDict *src = keyval_parse("src=1.5", NULL, &help, &error_abort);
+ Visitor *v, *alias_v;
+ Error *err = NULL;
+ double result = 0.0;
+
+ v = qobject_input_visitor_new_keyval(QOBJECT(src));
+ visit_start_struct(v, NULL, NULL, 0, &error_abort);
+
+ alias_v = visitor_forward_field(v, "dst", "src");
+ visit_type_number(alias_v, "src", &result, &err);
+ error_free_or_abort(&err);
+ visit_type_number(alias_v, "dst", &result, &err);
+ assert(result == 1.5);
+ assert(err == NULL);
+ visit_free(alias_v);
+
+ visit_end_struct(v, NULL);
+ visit_free(v);
+ qobject_unref(QOBJECT(src));
+}
+
+static void test_forward_string(void)
+{
+ char *dst = visit_with_forward("src=Hello",
+ CAST_VISIT_TYPE(visit_type_str));
+
+ g_assert_cmpstr(dst, ==, "Hello");
+ g_free(dst);
+}
+
+static void test_forward_struct(void)
+{
+ UserDefOne *dst = visit_with_forward("src.integer=42,src.string=Hello",
+ CAST_VISIT_TYPE(visit_type_UserDefOne));
+
+ g_assert_cmpint(dst->integer, ==, 42);
+ g_assert_cmpstr(dst->string, ==, "Hello");
+ g_assert_cmpint(dst->has_enum1, ==, false);
+ qapi_free_UserDefOne(dst);
+}
+
+static void test_forward_alternate(void)
+{
+ AltStrObj *s_dst = visit_with_forward("src=hello",
+ CAST_VISIT_TYPE(visit_type_AltStrObj));
+ AltStrObj *o_dst = visit_with_forward("src.integer=42,src.boolean=true,src.string=world",
+ CAST_VISIT_TYPE(visit_type_AltStrObj));
+
+ g_assert_cmpint(s_dst->type, ==, QTYPE_QSTRING);
+ g_assert_cmpstr(s_dst->u.s, ==, "hello");
+ g_assert_cmpint(o_dst->type, ==, QTYPE_QDICT);
+ g_assert_cmpint(o_dst->u.o.integer, ==, 42);
+ g_assert_cmpint(o_dst->u.o.boolean, ==, true);
+ g_assert_cmpstr(o_dst->u.o.string, ==, "world");
+
+ qapi_free_AltStrObj(s_dst);
+ qapi_free_AltStrObj(o_dst);
+}
+
+static void test_forward_list(void)
+{
+ uint8List *dst = visit_with_forward("src.0=1,src.1=2,src.2=3,src.3=4",
+ CAST_VISIT_TYPE(visit_type_uint8List));
+ uint8List *tmp;
+ int i;
+
+ for (tmp = dst, i = 1; i <= 4; i++) {
+ g_assert(tmp);
+ g_assert_cmpint(tmp->value, ==, i);
+ tmp = tmp->next;
+ }
+ g_assert(!tmp);
+ qapi_free_uint8List(dst);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/visitor/forward/struct", test_forward_struct);
+ g_test_add_func("/visitor/forward/alternate", test_forward_alternate);
+ g_test_add_func("/visitor/forward/string", test_forward_string);
+ g_test_add_func("/visitor/forward/size", test_forward_size);
+ g_test_add_func("/visitor/forward/number", test_forward_number);
+ g_test_add_func("/visitor/forward/any", test_forward_any);
+ g_test_add_func("/visitor/forward/list", test_forward_list);
+
+ return g_test_run();
+}