1 /*
2  * QAPI Forwarding Visitor unit-tests.
3  *
4  * Copyright (C) 2021 Red Hat Inc.
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  * See the COPYING file in the top-level directory.
8  */
9 
10 #include "qemu/osdep.h"
11 
12 #include "qapi/forward-visitor.h"
13 #include "qapi/qobject-input-visitor.h"
14 #include "qapi/error.h"
15 #include "qapi/qmp/qobject.h"
16 #include "qapi/qmp/qdict.h"
17 #include "test-qapi-visit.h"
18 #include "qemu/keyval.h"
19 
20 typedef bool GenericVisitor (Visitor *, const char *, void **, Error **);
21 #define CAST_VISIT_TYPE(fn) ((GenericVisitor *)(fn))
22 
23 /*
24  * Parse @srcstr and wrap it with a ForwardFieldVisitor converting "src" to
25  * "dst". Check that visiting the result with "src" name fails, and return
26  * the result of visiting "dst".
27  */
28 static void *visit_with_forward(const char *srcstr, GenericVisitor *fn)
29 {
30     bool help = false;
31     QDict *src = keyval_parse(srcstr, NULL, &help, &error_abort);
32     Visitor *v, *alias_v;
33     Error *err = NULL;
34     void *result = NULL;
35 
36     v = qobject_input_visitor_new_keyval(QOBJECT(src));
37     visit_start_struct(v, NULL, NULL, 0, &error_abort);
38 
39     alias_v = visitor_forward_field(v, "dst", "src");
40     fn(alias_v, "src", &result, &err);
41     error_free_or_abort(&err);
42     assert(!result);
43     fn(alias_v, "dst", &result, &err);
44     assert(err == NULL);
45     visit_free(alias_v);
46 
47     visit_end_struct(v, NULL);
48     visit_free(v);
49     qobject_unref(QOBJECT(src));
50     return result;
51 }
52 
53 static void test_forward_any(void)
54 {
55     QObject *src = visit_with_forward("src.integer=42,src.string=Hello,src.enum1=value2",
56                                       CAST_VISIT_TYPE(visit_type_any));
57     Visitor *v = qobject_input_visitor_new_keyval(src);
58     Error *err = NULL;
59     UserDefOne *dst;
60 
61     visit_type_UserDefOne(v, NULL, &dst, &err);
62     assert(err == NULL);
63     visit_free(v);
64 
65     g_assert_cmpint(dst->integer, ==, 42);
66     g_assert_cmpstr(dst->string, ==, "Hello");
67     g_assert_cmpint(dst->has_enum1, ==, true);
68     g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2);
69     qapi_free_UserDefOne(dst);
70     qobject_unref(QOBJECT(src));
71 }
72 
73 static void test_forward_size(void)
74 {
75     /*
76      * visit_type_size does not return a pointer, so visit_with_forward
77      * cannot be used.
78      */
79     bool help = false;
80     QDict *src = keyval_parse("src=1.5M", NULL, &help, &error_abort);
81     Visitor *v, *alias_v;
82     Error *err = NULL;
83     uint64_t result = 0;
84 
85     v = qobject_input_visitor_new_keyval(QOBJECT(src));
86     visit_start_struct(v, NULL, NULL, 0, &error_abort);
87 
88     alias_v = visitor_forward_field(v, "dst", "src");
89     visit_type_size(alias_v, "src", &result, &err);
90     error_free_or_abort(&err);
91     visit_type_size(alias_v, "dst", &result, &err);
92     assert(result == 3 << 19);
93     assert(err == NULL);
94     visit_free(alias_v);
95 
96     visit_end_struct(v, NULL);
97     visit_free(v);
98     qobject_unref(QOBJECT(src));
99 }
100 
101 static void test_forward_number(void)
102 {
103     /*
104      * visit_type_number does not return a pointer, so visit_with_forward
105      * cannot be used.
106      */
107     bool help = false;
108     QDict *src = keyval_parse("src=1.5", NULL, &help, &error_abort);
109     Visitor *v, *alias_v;
110     Error *err = NULL;
111     double result = 0.0;
112 
113     v = qobject_input_visitor_new_keyval(QOBJECT(src));
114     visit_start_struct(v, NULL, NULL, 0, &error_abort);
115 
116     alias_v = visitor_forward_field(v, "dst", "src");
117     visit_type_number(alias_v, "src", &result, &err);
118     error_free_or_abort(&err);
119     visit_type_number(alias_v, "dst", &result, &err);
120     assert(result == 1.5);
121     assert(err == NULL);
122     visit_free(alias_v);
123 
124     visit_end_struct(v, NULL);
125     visit_free(v);
126     qobject_unref(QOBJECT(src));
127 }
128 
129 static void test_forward_string(void)
130 {
131     char *dst = visit_with_forward("src=Hello",
132                                    CAST_VISIT_TYPE(visit_type_str));
133 
134     g_assert_cmpstr(dst, ==, "Hello");
135     g_free(dst);
136 }
137 
138 static void test_forward_struct(void)
139 {
140     UserDefOne *dst = visit_with_forward("src.integer=42,src.string=Hello",
141                                          CAST_VISIT_TYPE(visit_type_UserDefOne));
142 
143     g_assert_cmpint(dst->integer, ==, 42);
144     g_assert_cmpstr(dst->string, ==, "Hello");
145     g_assert_cmpint(dst->has_enum1, ==, false);
146     qapi_free_UserDefOne(dst);
147 }
148 
149 static void test_forward_alternate(void)
150 {
151     AltStrObj *s_dst = visit_with_forward("src=hello",
152                                           CAST_VISIT_TYPE(visit_type_AltStrObj));
153     AltStrObj *o_dst = visit_with_forward("src.integer=42,src.boolean=true,src.string=world",
154                                           CAST_VISIT_TYPE(visit_type_AltStrObj));
155 
156     g_assert_cmpint(s_dst->type, ==, QTYPE_QSTRING);
157     g_assert_cmpstr(s_dst->u.s, ==, "hello");
158     g_assert_cmpint(o_dst->type, ==, QTYPE_QDICT);
159     g_assert_cmpint(o_dst->u.o.integer, ==, 42);
160     g_assert_cmpint(o_dst->u.o.boolean, ==, true);
161     g_assert_cmpstr(o_dst->u.o.string, ==, "world");
162 
163     qapi_free_AltStrObj(s_dst);
164     qapi_free_AltStrObj(o_dst);
165 }
166 
167 static void test_forward_list(void)
168 {
169     uint8List *dst = visit_with_forward("src.0=1,src.1=2,src.2=3,src.3=4",
170                                         CAST_VISIT_TYPE(visit_type_uint8List));
171     uint8List *tmp;
172     int i;
173 
174     for (tmp = dst, i = 1; i <= 4; i++) {
175         g_assert(tmp);
176         g_assert_cmpint(tmp->value, ==, i);
177         tmp = tmp->next;
178     }
179     g_assert(!tmp);
180     qapi_free_uint8List(dst);
181 }
182 
183 int main(int argc, char **argv)
184 {
185     g_test_init(&argc, &argv, NULL);
186 
187     g_test_add_func("/visitor/forward/struct", test_forward_struct);
188     g_test_add_func("/visitor/forward/alternate", test_forward_alternate);
189     g_test_add_func("/visitor/forward/string", test_forward_string);
190     g_test_add_func("/visitor/forward/size", test_forward_size);
191     g_test_add_func("/visitor/forward/number", test_forward_number);
192     g_test_add_func("/visitor/forward/any", test_forward_any);
193     g_test_add_func("/visitor/forward/list", test_forward_list);
194 
195     return g_test_run();
196 }
197