1 /*
2  * QObject Output Visitor unit-tests.
3  *
4  * Copyright (C) 2011-2016 Red Hat Inc.
5  *
6  * Authors:
7  *  Luiz Capitulino <lcapitulino@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 
15 #include "qapi/error.h"
16 #include "qapi/qobject-output-visitor.h"
17 #include "test-qapi-visit.h"
18 #include "qapi/qmp/qbool.h"
19 #include "qapi/qmp/qdict.h"
20 #include "qapi/qmp/qlist.h"
21 #include "qapi/qmp/qnull.h"
22 #include "qapi/qmp/qnum.h"
23 #include "qapi/qmp/qstring.h"
24 
25 typedef struct TestOutputVisitorData {
26     Visitor *ov;
27     QObject *obj;
28 } TestOutputVisitorData;
29 
30 static void visitor_output_setup(TestOutputVisitorData *data,
31                                  const void *unused)
32 {
33     data->ov = qobject_output_visitor_new(&data->obj);
34     g_assert(data->ov);
35 }
36 
37 static void visitor_output_teardown(TestOutputVisitorData *data,
38                                     const void *unused)
39 {
40     visit_free(data->ov);
41     data->ov = NULL;
42     qobject_unref(data->obj);
43     data->obj = NULL;
44 }
45 
46 static QObject *visitor_get(TestOutputVisitorData *data)
47 {
48     visit_complete(data->ov, &data->obj);
49     g_assert(data->obj);
50     return data->obj;
51 }
52 
53 static void visitor_reset(TestOutputVisitorData *data)
54 {
55     visitor_output_teardown(data, NULL);
56     visitor_output_setup(data, NULL);
57 }
58 
59 static void test_visitor_out_int(TestOutputVisitorData *data,
60                                  const void *unused)
61 {
62     int64_t value = -42;
63     int64_t val;
64     QNum *qnum;
65 
66     visit_type_int(data->ov, NULL, &value, &error_abort);
67 
68     qnum = qobject_to(QNum, visitor_get(data));
69     g_assert(qnum);
70     g_assert(qnum_get_try_int(qnum, &val));
71     g_assert_cmpint(val, ==, value);
72 }
73 
74 static void test_visitor_out_bool(TestOutputVisitorData *data,
75                                   const void *unused)
76 {
77     bool value = true;
78     QBool *qbool;
79 
80     visit_type_bool(data->ov, NULL, &value, &error_abort);
81 
82     qbool = qobject_to(QBool, visitor_get(data));
83     g_assert(qbool);
84     g_assert(qbool_get_bool(qbool) == value);
85 }
86 
87 static void test_visitor_out_number(TestOutputVisitorData *data,
88                                     const void *unused)
89 {
90     double value = 3.14;
91     QNum *qnum;
92 
93     visit_type_number(data->ov, NULL, &value, &error_abort);
94 
95     qnum = qobject_to(QNum, visitor_get(data));
96     g_assert(qnum);
97     g_assert(qnum_get_double(qnum) == value);
98 }
99 
100 static void test_visitor_out_string(TestOutputVisitorData *data,
101                                     const void *unused)
102 {
103     char *string = (char *) "Q E M U";
104     QString *qstr;
105 
106     visit_type_str(data->ov, NULL, &string, &error_abort);
107 
108     qstr = qobject_to(QString, visitor_get(data));
109     g_assert(qstr);
110     g_assert_cmpstr(qstring_get_str(qstr), ==, string);
111 }
112 
113 static void test_visitor_out_no_string(TestOutputVisitorData *data,
114                                        const void *unused)
115 {
116     char *string = NULL;
117     QString *qstr;
118 
119     /* A null string should return "" */
120     visit_type_str(data->ov, NULL, &string, &error_abort);
121 
122     qstr = qobject_to(QString, visitor_get(data));
123     g_assert(qstr);
124     g_assert_cmpstr(qstring_get_str(qstr), ==, "");
125 }
126 
127 static void test_visitor_out_enum(TestOutputVisitorData *data,
128                                   const void *unused)
129 {
130     EnumOne i;
131     QString *qstr;
132 
133     for (i = 0; i < ENUM_ONE__MAX; i++) {
134         visit_type_EnumOne(data->ov, "unused", &i, &error_abort);
135 
136         qstr = qobject_to(QString, visitor_get(data));
137         g_assert(qstr);
138         g_assert_cmpstr(qstring_get_str(qstr), ==, EnumOne_str(i));
139         visitor_reset(data);
140     }
141 }
142 
143 static void test_visitor_out_struct(TestOutputVisitorData *data,
144                                     const void *unused)
145 {
146     TestStruct test_struct = { .integer = 42,
147                                .boolean = false,
148                                .string = (char *) "foo"};
149     TestStruct *p = &test_struct;
150     QDict *qdict;
151 
152     visit_type_TestStruct(data->ov, NULL, &p, &error_abort);
153 
154     qdict = qobject_to(QDict, visitor_get(data));
155     g_assert(qdict);
156     g_assert_cmpint(qdict_size(qdict), ==, 3);
157     g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42);
158     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, false);
159     g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo");
160 }
161 
162 static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
163                                            const void *unused)
164 {
165     int64_t value = 42;
166     UserDefTwo *ud2;
167     QDict *qdict, *dict1, *dict2, *dict3, *userdef;
168     const char *string = "user def string";
169     const char *strings[] = { "forty two", "forty three", "forty four",
170                               "forty five" };
171 
172     ud2 = g_malloc0(sizeof(*ud2));
173     ud2->string0 = g_strdup(strings[0]);
174 
175     ud2->dict1 = g_malloc0(sizeof(*ud2->dict1));
176     ud2->dict1->string1 = g_strdup(strings[1]);
177 
178     ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2));
179     ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1);
180     ud2->dict1->dict2->userdef->string = g_strdup(string);
181     ud2->dict1->dict2->userdef->integer = value;
182     ud2->dict1->dict2->string = g_strdup(strings[2]);
183 
184     ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3));
185     ud2->dict1->has_dict3 = true;
186     ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1);
187     ud2->dict1->dict3->userdef->string = g_strdup(string);
188     ud2->dict1->dict3->userdef->integer = value;
189     ud2->dict1->dict3->string = g_strdup(strings[3]);
190 
191     visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
192 
193     qdict = qobject_to(QDict, visitor_get(data));
194     g_assert(qdict);
195     g_assert_cmpint(qdict_size(qdict), ==, 2);
196     g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]);
197 
198     dict1 = qdict_get_qdict(qdict, "dict1");
199     g_assert_cmpint(qdict_size(dict1), ==, 3);
200     g_assert_cmpstr(qdict_get_str(dict1, "string1"), ==, strings[1]);
201 
202     dict2 = qdict_get_qdict(dict1, "dict2");
203     g_assert_cmpint(qdict_size(dict2), ==, 2);
204     g_assert_cmpstr(qdict_get_str(dict2, "string"), ==, strings[2]);
205     userdef = qdict_get_qdict(dict2, "userdef");
206     g_assert_cmpint(qdict_size(userdef), ==, 2);
207     g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
208     g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
209 
210     dict3 = qdict_get_qdict(dict1, "dict3");
211     g_assert_cmpint(qdict_size(dict3), ==, 2);
212     g_assert_cmpstr(qdict_get_str(dict3, "string"), ==, strings[3]);
213     userdef = qdict_get_qdict(dict3, "userdef");
214     g_assert_cmpint(qdict_size(userdef), ==, 2);
215     g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value);
216     g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string);
217 
218     qapi_free_UserDefTwo(ud2);
219 }
220 
221 static void test_visitor_out_list(TestOutputVisitorData *data,
222                                   const void *unused)
223 {
224     const char *value_str = "list value";
225     TestStruct *value;
226     TestStructList *head = NULL;
227     const int max_items = 10;
228     bool value_bool = true;
229     int value_int = 10;
230     QListEntry *entry;
231     QList *qlist;
232     int i;
233 
234     /* Build the list in reverse order... */
235     for (i = 0; i < max_items; i++) {
236         value = g_malloc0(sizeof(*value));
237         value->integer = value_int + (max_items - i - 1);
238         value->boolean = value_bool;
239         value->string = g_strdup(value_str);
240 
241         QAPI_LIST_PREPEND(head, value);
242     }
243 
244     visit_type_TestStructList(data->ov, NULL, &head, &error_abort);
245 
246     qlist = qobject_to(QList, visitor_get(data));
247     g_assert(qlist);
248     g_assert(!qlist_empty(qlist));
249 
250     /* ...and ensure that the visitor sees it in order */
251     i = 0;
252     QLIST_FOREACH_ENTRY(qlist, entry) {
253         QDict *qdict;
254 
255         qdict = qobject_to(QDict, entry->value);
256         g_assert(qdict);
257         g_assert_cmpint(qdict_size(qdict), ==, 3);
258         g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int + i);
259         g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool);
260         g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str);
261         i++;
262     }
263     g_assert_cmpint(i, ==, max_items);
264 
265     qapi_free_TestStructList(head);
266 }
267 
268 static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data,
269                                             const void *unused)
270 {
271     UserDefTwo *value;
272     UserDefTwoList *head = NULL;
273     const char string[] = "foo bar";
274     int i, max_count = 1024;
275 
276     for (i = 0; i < max_count; i++) {
277         value = g_malloc0(sizeof(*value));
278 
279         value->string0 = g_strdup(string);
280         value->dict1 = g_new0(UserDefTwoDict, 1);
281         value->dict1->string1 = g_strdup(string);
282         value->dict1->dict2 = g_new0(UserDefTwoDictDict, 1);
283         value->dict1->dict2->userdef = g_new0(UserDefOne, 1);
284         value->dict1->dict2->userdef->string = g_strdup(string);
285         value->dict1->dict2->userdef->integer = 42;
286         value->dict1->dict2->string = g_strdup(string);
287         value->dict1->has_dict3 = false;
288 
289         QAPI_LIST_PREPEND(head, value);
290     }
291 
292     qapi_free_UserDefTwoList(head);
293 }
294 
295 static void test_visitor_out_any(TestOutputVisitorData *data,
296                                  const void *unused)
297 {
298     QObject *qobj;
299     QNum *qnum;
300     QBool *qbool;
301     QString *qstring;
302     QDict *qdict;
303     int64_t val;
304 
305     qobj = QOBJECT(qnum_from_int(-42));
306     visit_type_any(data->ov, NULL, &qobj, &error_abort);
307     qnum = qobject_to(QNum, visitor_get(data));
308     g_assert(qnum);
309     g_assert(qnum_get_try_int(qnum, &val));
310     g_assert_cmpint(val, ==, -42);
311     qobject_unref(qobj);
312 
313     visitor_reset(data);
314     qdict = qdict_new();
315     qdict_put_int(qdict, "integer", -42);
316     qdict_put_bool(qdict, "boolean", true);
317     qdict_put_str(qdict, "string", "foo");
318     qobj = QOBJECT(qdict);
319     visit_type_any(data->ov, NULL, &qobj, &error_abort);
320     qobject_unref(qobj);
321     qdict = qobject_to(QDict, visitor_get(data));
322     g_assert(qdict);
323     qnum = qobject_to(QNum, qdict_get(qdict, "integer"));
324     g_assert(qnum);
325     g_assert(qnum_get_try_int(qnum, &val));
326     g_assert_cmpint(val, ==, -42);
327     qbool = qobject_to(QBool, qdict_get(qdict, "boolean"));
328     g_assert(qbool);
329     g_assert(qbool_get_bool(qbool) == true);
330     qstring = qobject_to(QString, qdict_get(qdict, "string"));
331     g_assert(qstring);
332     g_assert_cmpstr(qstring_get_str(qstring), ==, "foo");
333 }
334 
335 static void test_visitor_out_union_flat(TestOutputVisitorData *data,
336                                         const void *unused)
337 {
338     QDict *qdict;
339 
340     UserDefFlatUnion *tmp = g_new0(UserDefFlatUnion, 1);
341     tmp->enum1 = ENUM_ONE_VALUE1;
342     tmp->string = g_strdup("str");
343     tmp->integer = 41;
344     tmp->u.value1.boolean = true;
345 
346     visit_type_UserDefFlatUnion(data->ov, NULL, &tmp, &error_abort);
347     qdict = qobject_to(QDict, visitor_get(data));
348     g_assert(qdict);
349     g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1");
350     g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str");
351     g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 41);
352     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);
353 
354     qapi_free_UserDefFlatUnion(tmp);
355 }
356 
357 static void test_visitor_out_alternate(TestOutputVisitorData *data,
358                                        const void *unused)
359 {
360     UserDefAlternate *tmp;
361     QNum *qnum;
362     QString *qstr;
363     QDict *qdict;
364     int64_t val;
365 
366     tmp = g_new0(UserDefAlternate, 1);
367     tmp->type = QTYPE_QNUM;
368     tmp->u.i = 42;
369 
370     visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
371     qnum = qobject_to(QNum, visitor_get(data));
372     g_assert(qnum);
373     g_assert(qnum_get_try_int(qnum, &val));
374     g_assert_cmpint(val, ==, 42);
375 
376     qapi_free_UserDefAlternate(tmp);
377 
378     visitor_reset(data);
379     tmp = g_new0(UserDefAlternate, 1);
380     tmp->type = QTYPE_QSTRING;
381     tmp->u.e = ENUM_ONE_VALUE1;
382 
383     visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
384     qstr = qobject_to(QString, visitor_get(data));
385     g_assert(qstr);
386     g_assert_cmpstr(qstring_get_str(qstr), ==, "value1");
387 
388     qapi_free_UserDefAlternate(tmp);
389 
390     visitor_reset(data);
391     tmp = g_new0(UserDefAlternate, 1);
392     tmp->type = QTYPE_QNULL;
393     tmp->u.n = qnull();
394 
395     visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
396     g_assert_cmpint(qobject_type(visitor_get(data)), ==, QTYPE_QNULL);
397 
398     qapi_free_UserDefAlternate(tmp);
399 
400     visitor_reset(data);
401     tmp = g_new0(UserDefAlternate, 1);
402     tmp->type = QTYPE_QDICT;
403     tmp->u.udfu.integer = 1;
404     tmp->u.udfu.string = g_strdup("str");
405     tmp->u.udfu.enum1 = ENUM_ONE_VALUE1;
406     tmp->u.udfu.u.value1.boolean = true;
407 
408     visit_type_UserDefAlternate(data->ov, NULL, &tmp, &error_abort);
409     qdict = qobject_to(QDict, visitor_get(data));
410     g_assert(qdict);
411     g_assert_cmpint(qdict_size(qdict), ==, 4);
412     g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 1);
413     g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "str");
414     g_assert_cmpstr(qdict_get_str(qdict, "enum1"), ==, "value1");
415     g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, true);
416 
417     qapi_free_UserDefAlternate(tmp);
418 }
419 
420 static void test_visitor_out_null(TestOutputVisitorData *data,
421                                   const void *unused)
422 {
423     QNull *null = NULL;
424     QDict *qdict;
425     QObject *nil;
426 
427     visit_start_struct(data->ov, NULL, NULL, 0, &error_abort);
428     visit_type_null(data->ov, "a", &null, &error_abort);
429     visit_check_struct(data->ov, &error_abort);
430     visit_end_struct(data->ov, NULL);
431     qdict = qobject_to(QDict, visitor_get(data));
432     g_assert(qdict);
433     g_assert_cmpint(qdict_size(qdict), ==, 1);
434     nil = qdict_get(qdict, "a");
435     g_assert(nil);
436     g_assert(qobject_type(nil) == QTYPE_QNULL);
437 }
438 
439 static void test_visitor_out_list_struct(TestOutputVisitorData *data,
440                                          const void *unused)
441 {
442     const char *int_member[] = {
443         "integer", "s8", "s16", "s32", "s64", "u8", "u16", "u32", "u64" };
444     g_autoptr(ArrayStruct) arrs = g_new0(ArrayStruct, 1);
445     int i, j;
446     QDict *qdict;
447     QList *qlist;
448     QListEntry *e;
449 
450     for (i = 31; i >= 0; i--) {
451         QAPI_LIST_PREPEND(arrs->integer, i);
452     }
453 
454     for (i = 31; i >= 0; i--) {
455         QAPI_LIST_PREPEND(arrs->s8, i);
456     }
457 
458     for (i = 31; i >= 0; i--) {
459         QAPI_LIST_PREPEND(arrs->s16, i);
460     }
461 
462     for (i = 31; i >= 0; i--) {
463         QAPI_LIST_PREPEND(arrs->s32, i);
464     }
465 
466     for (i = 31; i >= 0; i--) {
467         QAPI_LIST_PREPEND(arrs->s64, i);
468     }
469 
470     for (i = 31; i >= 0; i--) {
471         QAPI_LIST_PREPEND(arrs->u8, i);
472     }
473 
474     for (i = 31; i >= 0; i--) {
475         QAPI_LIST_PREPEND(arrs->u16, i);
476     }
477 
478     for (i = 31; i >= 0; i--) {
479         QAPI_LIST_PREPEND(arrs->u32, i);
480     }
481 
482     for (i = 31; i >= 0; i--) {
483         QAPI_LIST_PREPEND(arrs->u64, i);
484     }
485 
486     for (i = 31; i >= 0; i--) {
487         QAPI_LIST_PREPEND(arrs->number, (double)i / 3);
488     }
489 
490     for (i = 31; i >= 0; i--) {
491         QAPI_LIST_PREPEND(arrs->boolean, QEMU_IS_ALIGNED(i, 3));
492     }
493 
494     for (i = 31; i >= 0; i--) {
495         QAPI_LIST_PREPEND(arrs->string, g_strdup_printf("%d", i));
496     }
497 
498     visit_type_ArrayStruct(data->ov, NULL, &arrs, &error_abort);
499 
500     qdict = qobject_to(QDict, visitor_get(data));
501     g_assert(qdict);
502 
503     for (i = 0; i < G_N_ELEMENTS(int_member); i++) {
504         qlist = qdict_get_qlist(qdict, int_member[i]);
505         g_assert(qlist);
506         j = 0;
507         QLIST_FOREACH_ENTRY(qlist, e) {
508             QNum *qvalue = qobject_to(QNum, qlist_entry_obj(e));
509             g_assert(qvalue);
510             g_assert_cmpint(qnum_get_int(qvalue), ==, j);
511             j++;
512         }
513     }
514 
515     qlist = qdict_get_qlist(qdict, "number");
516     g_assert(qlist);
517     i = 0;
518     QLIST_FOREACH_ENTRY(qlist, e) {
519         QNum *qvalue = qobject_to(QNum, qlist_entry_obj(e));
520         char expected[32], actual[32];
521 
522         g_assert(qvalue);
523         sprintf(expected, "%.6f", (double)i / 3);
524         sprintf(actual, "%.6f", qnum_get_double(qvalue));
525         g_assert_cmpstr(actual, ==, expected);
526         i++;
527     }
528 
529     qlist = qdict_get_qlist(qdict, "boolean");
530     g_assert(qlist);
531     i = 0;
532     QLIST_FOREACH_ENTRY(qlist, e) {
533         QBool *qvalue = qobject_to(QBool, qlist_entry_obj(e));
534         g_assert(qvalue);
535         g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0);
536         i++;
537     }
538 
539     qlist = qdict_get_qlist(qdict, "string");
540     g_assert(qlist);
541     i = 0;
542     QLIST_FOREACH_ENTRY(qlist, e) {
543         QString *qvalue = qobject_to(QString, qlist_entry_obj(e));
544         char expected[32];
545 
546         g_assert(qvalue);
547         sprintf(expected, "%d", i);
548         g_assert_cmpstr(qstring_get_str(qvalue), ==, expected);
549         i++;
550     }
551 }
552 
553 static void output_visitor_test_add(const char *testpath,
554                                     TestOutputVisitorData *data,
555                                     void (*test_func)(TestOutputVisitorData *data, const void *user_data))
556 {
557     g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup,
558                test_func, visitor_output_teardown);
559 }
560 
561 int main(int argc, char **argv)
562 {
563     TestOutputVisitorData out_visitor_data;
564 
565     g_test_init(&argc, &argv, NULL);
566 
567     output_visitor_test_add("/visitor/output/int",
568                             &out_visitor_data, test_visitor_out_int);
569     output_visitor_test_add("/visitor/output/bool",
570                             &out_visitor_data, test_visitor_out_bool);
571     output_visitor_test_add("/visitor/output/number",
572                             &out_visitor_data, test_visitor_out_number);
573     output_visitor_test_add("/visitor/output/string",
574                             &out_visitor_data, test_visitor_out_string);
575     output_visitor_test_add("/visitor/output/no-string",
576                             &out_visitor_data, test_visitor_out_no_string);
577     output_visitor_test_add("/visitor/output/enum",
578                             &out_visitor_data, test_visitor_out_enum);
579     output_visitor_test_add("/visitor/output/struct",
580                             &out_visitor_data, test_visitor_out_struct);
581     output_visitor_test_add("/visitor/output/struct-nested",
582                             &out_visitor_data, test_visitor_out_struct_nested);
583     output_visitor_test_add("/visitor/output/list",
584                             &out_visitor_data, test_visitor_out_list);
585     output_visitor_test_add("/visitor/output/any",
586                             &out_visitor_data, test_visitor_out_any);
587     output_visitor_test_add("/visitor/output/list-qapi-free",
588                             &out_visitor_data, test_visitor_out_list_qapi_free);
589     output_visitor_test_add("/visitor/output/union-flat",
590                             &out_visitor_data, test_visitor_out_union_flat);
591     output_visitor_test_add("/visitor/output/alternate",
592                             &out_visitor_data, test_visitor_out_alternate);
593     output_visitor_test_add("/visitor/output/null",
594                             &out_visitor_data, test_visitor_out_null);
595     output_visitor_test_add("/visitor/output/list_struct",
596                             &out_visitor_data, test_visitor_out_list_struct);
597 
598     g_test_run();
599 
600     return 0;
601 }
602