aboutsummaryrefslogtreecommitdiff
path: root/tests/unit
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2021-07-18 08:49:22 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2021-07-23 18:17:17 +0200
commit18fa3ebc45262cebc7c9f0ba6f717452bdc51db7 (patch)
treea53611506176b3f6b20ef7a9c14bac6a07d33ddf /tests/unit
parenteafadbbbac06a8d72baa976f4d3c42b0e5f8cfc8 (diff)
downloadqemu-18fa3ebc45262cebc7c9f0ba6f717452bdc51db7.zip
qemu-18fa3ebc45262cebc7c9f0ba6f717452bdc51db7.tar.gz
qemu-18fa3ebc45262cebc7c9f0ba6f717452bdc51db7.tar.bz2
qapi: introduce forwarding visitor
This new adaptor visitor takes a single field of the adaptee, and exposes it with a different name. This will be used for QOM alias properties. Alias targets can of course have a different name than the alias property itself (e.g. a machine's pflash0 might be an alias of a property named 'drive'). When the target's getter or setter invokes the visitor, it will use a different name than what the caller expects, and the visitor will not be able to find it (or will consume erroneously). The solution is for alias getters and setters to wrap the incoming visitor, and forward the sole field that the target is expecting while renaming it appropriately. Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
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 3e0504d..5736d28 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 0000000..348f7e4
--- /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();
+}