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