xref: /qemu/tests/qtest/device-introspect-test.c (revision 407bc4bf9027f7ac4333e47cd900d773b99a23e3)
12d1abb85SMarkus Armbruster /*
22d1abb85SMarkus Armbruster  * Device introspection test cases
32d1abb85SMarkus Armbruster  *
42d1abb85SMarkus Armbruster  * Copyright (c) 2015 Red Hat Inc.
52d1abb85SMarkus Armbruster  *
62d1abb85SMarkus Armbruster  * Authors:
72d1abb85SMarkus Armbruster  *  Markus Armbruster <armbru@redhat.com>,
82d1abb85SMarkus Armbruster  *
92d1abb85SMarkus Armbruster  * This work is licensed under the terms of the GNU GPL, version 2 or later.
102d1abb85SMarkus Armbruster  * See the COPYING file in the top-level directory.
112d1abb85SMarkus Armbruster  */
122d1abb85SMarkus Armbruster 
132d1abb85SMarkus Armbruster /*
142d1abb85SMarkus Armbruster  * Covers QMP device-list-properties and HMP device_add help.  We
152d1abb85SMarkus Armbruster  * currently don't check that their output makes sense, only that QEMU
162d1abb85SMarkus Armbruster  * survives.  Useful since we've had an astounding number of crash
172d1abb85SMarkus Armbruster  * bugs around here.
182d1abb85SMarkus Armbruster  */
192d1abb85SMarkus Armbruster 
20681c28a3SPeter Maydell #include "qemu/osdep.h"
21*407bc4bfSDaniel P. Berrangé #include "qobject/qstring.h"
22*407bc4bfSDaniel P. Berrangé #include "qobject/qdict.h"
23*407bc4bfSDaniel P. Berrangé #include "qobject/qlist.h"
24907b5105SMarc-André Lureau #include "libqtest.h"
252d1abb85SMarkus Armbruster 
262d1abb85SMarkus Armbruster const char common_args[] = "-nodefaults -machine none";
272d1abb85SMarkus Armbruster 
qom_list_types(QTestState * qts,const char * implements,bool abstract)2839fb795aSThomas Huth static QList *qom_list_types(QTestState * qts, const char *implements,
2939fb795aSThomas Huth                              bool abstract)
302d1abb85SMarkus Armbruster {
312d1abb85SMarkus Armbruster     QDict *resp;
322d1abb85SMarkus Armbruster     QList *ret;
331c6d75d5SEduardo Habkost     QDict *args = qdict_new();
342d1abb85SMarkus Armbruster 
3546f5ac20SEric Blake     qdict_put_bool(args, "abstract", abstract);
361c6d75d5SEduardo Habkost     if (implements) {
3746f5ac20SEric Blake         qdict_put_str(args, "implements", implements);
381c6d75d5SEduardo Habkost     }
3939fb795aSThomas Huth     resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }",
4039fb795aSThomas Huth                      args);
412d1abb85SMarkus Armbruster     g_assert(qdict_haskey(resp, "return"));
422d1abb85SMarkus Armbruster     ret = qdict_get_qlist(resp, "return");
43cb3e7f08SMarc-André Lureau     qobject_ref(ret);
44cb3e7f08SMarc-André Lureau     qobject_unref(resp);
452d1abb85SMarkus Armbruster     return ret;
462d1abb85SMarkus Armbruster }
472d1abb85SMarkus Armbruster 
48f86285c5SEduardo Habkost /* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */
qom_type_index(QList * types)49f86285c5SEduardo Habkost static QDict *qom_type_index(QList *types)
50f86285c5SEduardo Habkost {
51f86285c5SEduardo Habkost     QDict *index = qdict_new();
52f86285c5SEduardo Habkost     QListEntry *e;
53f86285c5SEduardo Habkost 
54f86285c5SEduardo Habkost     QLIST_FOREACH_ENTRY(types, e) {
557dc847ebSMax Reitz         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
56f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
57cb3e7f08SMarc-André Lureau         qobject_ref(d);
58f86285c5SEduardo Habkost         qdict_put(index, name, d);
59f86285c5SEduardo Habkost     }
60f86285c5SEduardo Habkost     return index;
61f86285c5SEduardo Habkost }
62f86285c5SEduardo Habkost 
63f86285c5SEduardo Habkost /* Check if @parent is present in the parent chain of @type */
qom_has_parent(QDict * index,const char * type,const char * parent)64f86285c5SEduardo Habkost static bool qom_has_parent(QDict *index, const char *type, const char *parent)
65f86285c5SEduardo Habkost {
66f86285c5SEduardo Habkost     while (type) {
67f86285c5SEduardo Habkost         QDict *d = qdict_get_qdict(index, type);
68f86285c5SEduardo Habkost         const char *p = d && qdict_haskey(d, "parent") ?
69f86285c5SEduardo Habkost                         qdict_get_str(d, "parent") :
70f86285c5SEduardo Habkost                         NULL;
71f86285c5SEduardo Habkost 
72f86285c5SEduardo Habkost         if (!strcmp(type, parent)) {
73f86285c5SEduardo Habkost             return true;
74f86285c5SEduardo Habkost         }
75f86285c5SEduardo Habkost 
76f86285c5SEduardo Habkost         type = p;
77f86285c5SEduardo Habkost     }
78f86285c5SEduardo Habkost 
79f86285c5SEduardo Habkost     return false;
80f86285c5SEduardo Habkost }
81f86285c5SEduardo Habkost 
82dbb2a604SEduardo Habkost /* Find an entry on a list returned by qom-list-types */
type_list_find(QList * types,const char * name)83dbb2a604SEduardo Habkost static QDict *type_list_find(QList *types, const char *name)
84dbb2a604SEduardo Habkost {
85dbb2a604SEduardo Habkost     QListEntry *e;
86dbb2a604SEduardo Habkost 
87dbb2a604SEduardo Habkost     QLIST_FOREACH_ENTRY(types, e) {
887dc847ebSMax Reitz         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
89dbb2a604SEduardo Habkost         const char *ename = qdict_get_str(d, "name");
90dbb2a604SEduardo Habkost         if (!strcmp(ename, name)) {
91dbb2a604SEduardo Habkost             return d;
92dbb2a604SEduardo Habkost         }
93dbb2a604SEduardo Habkost     }
94dbb2a604SEduardo Habkost 
95dbb2a604SEduardo Habkost     return NULL;
96dbb2a604SEduardo Habkost }
97dbb2a604SEduardo Habkost 
device_type_list(QTestState * qts,bool abstract)9839fb795aSThomas Huth static QList *device_type_list(QTestState *qts, bool abstract)
991c6d75d5SEduardo Habkost {
10039fb795aSThomas Huth     return qom_list_types(qts, "device", abstract);
1011c6d75d5SEduardo Habkost }
1021c6d75d5SEduardo Habkost 
test_one_device(QTestState * qts,const char * type)10339fb795aSThomas Huth static void test_one_device(QTestState *qts, const char *type)
1042d1abb85SMarkus Armbruster {
1052d1abb85SMarkus Armbruster     QDict *resp;
106e27bd498SPaolo Bonzini     char *help, *escaped;
107e27bd498SPaolo Bonzini     GRegex *comma;
108d0685212SThomas Huth 
109d0685212SThomas Huth     g_test_message("Testing device '%s'", type);
110d0685212SThomas Huth 
11139fb795aSThomas Huth     resp = qtest_qmp(qts, "{'execute': 'device-list-properties',"
1122d1abb85SMarkus Armbruster                           " 'arguments': {'typename': %s}}",
1132d1abb85SMarkus Armbruster                type);
114cb3e7f08SMarc-André Lureau     qobject_unref(resp);
1152d1abb85SMarkus Armbruster 
116e27bd498SPaolo Bonzini     comma = g_regex_new(",", 0, 0, NULL);
117e27bd498SPaolo Bonzini     escaped = g_regex_replace_literal(comma, type, -1, 0, ",,", 0, NULL);
118e27bd498SPaolo Bonzini     g_regex_unref(comma);
119e27bd498SPaolo Bonzini 
120e27bd498SPaolo Bonzini     help = qtest_hmp(qts, "device_add \"%s,help\"", escaped);
1212d1abb85SMarkus Armbruster     g_free(help);
122e27bd498SPaolo Bonzini     g_free(escaped);
1232d1abb85SMarkus Armbruster }
1242d1abb85SMarkus Armbruster 
test_device_intro_list(void)1252d1abb85SMarkus Armbruster static void test_device_intro_list(void)
1262d1abb85SMarkus Armbruster {
1272d1abb85SMarkus Armbruster     QList *types;
1282d1abb85SMarkus Armbruster     char *help;
12939fb795aSThomas Huth     QTestState *qts;
1302d1abb85SMarkus Armbruster 
13139fb795aSThomas Huth     qts = qtest_init(common_args);
1322d1abb85SMarkus Armbruster 
13339fb795aSThomas Huth     types = device_type_list(qts, true);
134cb3e7f08SMarc-André Lureau     qobject_unref(types);
1352d1abb85SMarkus Armbruster 
13639fb795aSThomas Huth     help = qtest_hmp(qts, "device_add help");
1372d1abb85SMarkus Armbruster     g_free(help);
1382d1abb85SMarkus Armbruster 
13939fb795aSThomas Huth     qtest_quit(qts);
1402d1abb85SMarkus Armbruster }
1412d1abb85SMarkus Armbruster 
142f86285c5SEduardo Habkost /*
143f86285c5SEduardo Habkost  * Ensure all entries returned by qom-list-types implements=<parent>
144f86285c5SEduardo Habkost  * have <parent> as a parent.
145f86285c5SEduardo Habkost  */
test_qom_list_parents(QTestState * qts,const char * parent)14639fb795aSThomas Huth static void test_qom_list_parents(QTestState *qts, const char *parent)
147f86285c5SEduardo Habkost {
148f86285c5SEduardo Habkost     QList *types;
149f86285c5SEduardo Habkost     QListEntry *e;
150f86285c5SEduardo Habkost     QDict *index;
151f86285c5SEduardo Habkost 
15239fb795aSThomas Huth     types = qom_list_types(qts, parent, true);
153f86285c5SEduardo Habkost     index = qom_type_index(types);
154f86285c5SEduardo Habkost 
155f86285c5SEduardo Habkost     QLIST_FOREACH_ENTRY(types, e) {
1567dc847ebSMax Reitz         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
157f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
158f86285c5SEduardo Habkost 
159f86285c5SEduardo Habkost         g_assert(qom_has_parent(index, name, parent));
160f86285c5SEduardo Habkost     }
161f86285c5SEduardo Habkost 
162cb3e7f08SMarc-André Lureau     qobject_unref(types);
163cb3e7f08SMarc-André Lureau     qobject_unref(index);
164f86285c5SEduardo Habkost }
165f86285c5SEduardo Habkost 
test_qom_list_fields(void)16687467eaeSEduardo Habkost static void test_qom_list_fields(void)
16787467eaeSEduardo Habkost {
16887467eaeSEduardo Habkost     QList *all_types;
16987467eaeSEduardo Habkost     QList *non_abstract;
17087467eaeSEduardo Habkost     QListEntry *e;
17139fb795aSThomas Huth     QTestState *qts;
17287467eaeSEduardo Habkost 
17339fb795aSThomas Huth     qts = qtest_init(common_args);
17487467eaeSEduardo Habkost 
17539fb795aSThomas Huth     all_types = qom_list_types(qts, NULL, true);
17639fb795aSThomas Huth     non_abstract = qom_list_types(qts, NULL, false);
17787467eaeSEduardo Habkost 
17887467eaeSEduardo Habkost     QLIST_FOREACH_ENTRY(all_types, e) {
1797dc847ebSMax Reitz         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
18087467eaeSEduardo Habkost         const char *name = qdict_get_str(d, "name");
18187467eaeSEduardo Habkost         bool abstract = qdict_haskey(d, "abstract") ?
18287467eaeSEduardo Habkost                         qdict_get_bool(d, "abstract") :
18387467eaeSEduardo Habkost                         false;
18487467eaeSEduardo Habkost         bool expected_abstract = !type_list_find(non_abstract, name);
18587467eaeSEduardo Habkost 
18687467eaeSEduardo Habkost         g_assert(abstract == expected_abstract);
18787467eaeSEduardo Habkost     }
18887467eaeSEduardo Habkost 
18939fb795aSThomas Huth     test_qom_list_parents(qts, "object");
19039fb795aSThomas Huth     test_qom_list_parents(qts, "device");
19139fb795aSThomas Huth     test_qom_list_parents(qts, "sys-bus-device");
192f86285c5SEduardo Habkost 
193cb3e7f08SMarc-André Lureau     qobject_unref(all_types);
194cb3e7f08SMarc-André Lureau     qobject_unref(non_abstract);
19539fb795aSThomas Huth     qtest_quit(qts);
19687467eaeSEduardo Habkost }
19787467eaeSEduardo Habkost 
test_device_intro_none(void)1982d1abb85SMarkus Armbruster static void test_device_intro_none(void)
1992d1abb85SMarkus Armbruster {
20039fb795aSThomas Huth     QTestState *qts = qtest_init(common_args);
2013e7b80f8SDaniel P. Berrangé     g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
2023e7b80f8SDaniel P. Berrangé     g_autofree char *qom_tree_end = NULL;
2033e7b80f8SDaniel P. Berrangé     g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
2043e7b80f8SDaniel P. Berrangé     g_autofree char *qtree_end = NULL;
20539fb795aSThomas Huth 
20639fb795aSThomas Huth     test_one_device(qts, "nonexistent");
2073e7b80f8SDaniel P. Berrangé 
2083e7b80f8SDaniel P. Berrangé     /* Make sure that really nothing changed in the trees */
2093e7b80f8SDaniel P. Berrangé     qom_tree_end = qtest_hmp(qts, "info qom-tree");
2103e7b80f8SDaniel P. Berrangé     g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
2113e7b80f8SDaniel P. Berrangé     qtree_end = qtest_hmp(qts, "info qtree");
2123e7b80f8SDaniel P. Berrangé     g_assert_cmpstr(qtree_start, ==, qtree_end);
2133e7b80f8SDaniel P. Berrangé 
21439fb795aSThomas Huth     qtest_quit(qts);
2152d1abb85SMarkus Armbruster }
2162d1abb85SMarkus Armbruster 
test_device_intro_abstract(void)2172d1abb85SMarkus Armbruster static void test_device_intro_abstract(void)
2182d1abb85SMarkus Armbruster {
21939fb795aSThomas Huth     QTestState *qts = qtest_init(common_args);
2203e7b80f8SDaniel P. Berrangé     g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
2213e7b80f8SDaniel P. Berrangé     g_autofree char *qom_tree_end = NULL;
2223e7b80f8SDaniel P. Berrangé     g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
2233e7b80f8SDaniel P. Berrangé     g_autofree char *qtree_end = NULL;
22439fb795aSThomas Huth 
22539fb795aSThomas Huth     test_one_device(qts, "device");
2263e7b80f8SDaniel P. Berrangé 
2273e7b80f8SDaniel P. Berrangé     /* Make sure that really nothing changed in the trees */
2283e7b80f8SDaniel P. Berrangé     qom_tree_end = qtest_hmp(qts, "info qom-tree");
2293e7b80f8SDaniel P. Berrangé     g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
2303e7b80f8SDaniel P. Berrangé     qtree_end = qtest_hmp(qts, "info qtree");
2313e7b80f8SDaniel P. Berrangé     g_assert_cmpstr(qtree_start, ==, qtree_end);
2323e7b80f8SDaniel P. Berrangé 
23339fb795aSThomas Huth     qtest_quit(qts);
2342d1abb85SMarkus Armbruster }
2352d1abb85SMarkus Armbruster 
test_device_intro_concrete(const void * args)236410573aaSThomas Huth static void test_device_intro_concrete(const void *args)
2372d1abb85SMarkus Armbruster {
2382d1abb85SMarkus Armbruster     QList *types;
2392d1abb85SMarkus Armbruster     QListEntry *entry;
2402d1abb85SMarkus Armbruster     const char *type;
2413e7b80f8SDaniel P. Berrangé     QTestState *qts = qtest_init(args);
2423e7b80f8SDaniel P. Berrangé     g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
2433e7b80f8SDaniel P. Berrangé     g_autofree char *qom_tree_end = NULL;
2443e7b80f8SDaniel P. Berrangé     g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
2453e7b80f8SDaniel P. Berrangé     g_autofree char *qtree_end = NULL;
2462d1abb85SMarkus Armbruster 
24739fb795aSThomas Huth     types = device_type_list(qts, false);
2482d1abb85SMarkus Armbruster 
2492d1abb85SMarkus Armbruster     QLIST_FOREACH_ENTRY(types, entry) {
2507dc847ebSMax Reitz         type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)),
2512d1abb85SMarkus Armbruster                                  "name");
2522d1abb85SMarkus Armbruster         g_assert(type);
25339fb795aSThomas Huth         test_one_device(qts, type);
2542d1abb85SMarkus Armbruster     }
2552d1abb85SMarkus Armbruster 
2563e7b80f8SDaniel P. Berrangé     /*
2573e7b80f8SDaniel P. Berrangé      * Some devices leave dangling pointers in QOM behind.
2583e7b80f8SDaniel P. Berrangé      * "info qom-tree" or "info qtree" have a good chance at crashing then.
2593e7b80f8SDaniel P. Berrangé      * Also make sure that the tree did not change.
2603e7b80f8SDaniel P. Berrangé      */
2613e7b80f8SDaniel P. Berrangé     qom_tree_end = qtest_hmp(qts, "info qom-tree");
2623e7b80f8SDaniel P. Berrangé     g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
2633e7b80f8SDaniel P. Berrangé 
2643e7b80f8SDaniel P. Berrangé     qtree_end = qtest_hmp(qts, "info qtree");
2653e7b80f8SDaniel P. Berrangé     g_assert_cmpstr(qtree_start, ==, qtree_end);
2663e7b80f8SDaniel P. Berrangé 
267cb3e7f08SMarc-André Lureau     qobject_unref(types);
26839fb795aSThomas Huth     qtest_quit(qts);
2692d1abb85SMarkus Armbruster }
2702d1abb85SMarkus Armbruster 
test_abstract_interfaces(void)2711c6d75d5SEduardo Habkost static void test_abstract_interfaces(void)
2721c6d75d5SEduardo Habkost {
2731c6d75d5SEduardo Habkost     QList *all_types;
274dbb2a604SEduardo Habkost     QListEntry *e;
275f86285c5SEduardo Habkost     QDict *index;
27639fb795aSThomas Huth     QTestState *qts;
2771c6d75d5SEduardo Habkost 
27839fb795aSThomas Huth     qts = qtest_init(common_args);
279f86285c5SEduardo Habkost 
28039fb795aSThomas Huth     all_types = qom_list_types(qts, "interface", true);
281f86285c5SEduardo Habkost     index = qom_type_index(all_types);
2821c6d75d5SEduardo Habkost 
283dbb2a604SEduardo Habkost     QLIST_FOREACH_ENTRY(all_types, e) {
2847dc847ebSMax Reitz         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
285f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
2861c6d75d5SEduardo Habkost 
287f86285c5SEduardo Habkost         /*
288f86285c5SEduardo Habkost          * qom-list-types implements=interface returns all types
289f86285c5SEduardo Habkost          * that implement _any_ interface (not just interface
290f86285c5SEduardo Habkost          * types), so skip the ones that don't have "interface"
291f86285c5SEduardo Habkost          * on the parent type chain.
292f86285c5SEduardo Habkost          */
293f86285c5SEduardo Habkost         if (!qom_has_parent(index, name, "interface")) {
29487467eaeSEduardo Habkost             /* Not an interface type */
29587467eaeSEduardo Habkost             continue;
29687467eaeSEduardo Habkost         }
2971c6d75d5SEduardo Habkost 
29887467eaeSEduardo Habkost         g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract"));
2991c6d75d5SEduardo Habkost     }
3001c6d75d5SEduardo Habkost 
301cb3e7f08SMarc-André Lureau     qobject_unref(all_types);
302cb3e7f08SMarc-André Lureau     qobject_unref(index);
30339fb795aSThomas Huth     qtest_quit(qts);
3041c6d75d5SEduardo Habkost }
3051c6d75d5SEduardo Habkost 
add_machine_test_case(const char * mname)306410573aaSThomas Huth static void add_machine_test_case(const char *mname)
307410573aaSThomas Huth {
308410573aaSThomas Huth     char *path, *args;
309410573aaSThomas Huth 
310410573aaSThomas Huth     path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname);
311410573aaSThomas Huth     args = g_strdup_printf("-M %s", mname);
312eefd26b8SAkihiko Odaki     qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free);
313410573aaSThomas Huth     g_free(path);
314410573aaSThomas Huth 
315410573aaSThomas Huth     path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname);
316410573aaSThomas Huth     args = g_strdup_printf("-nodefaults -M %s", mname);
317eefd26b8SAkihiko Odaki     qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free);
318410573aaSThomas Huth     g_free(path);
319410573aaSThomas Huth }
320410573aaSThomas Huth 
main(int argc,char ** argv)3212d1abb85SMarkus Armbruster int main(int argc, char **argv)
3222d1abb85SMarkus Armbruster {
3232d1abb85SMarkus Armbruster     g_test_init(&argc, &argv, NULL);
3242d1abb85SMarkus Armbruster 
3252d1abb85SMarkus Armbruster     qtest_add_func("device/introspect/list", test_device_intro_list);
32687467eaeSEduardo Habkost     qtest_add_func("device/introspect/list-fields", test_qom_list_fields);
3272d1abb85SMarkus Armbruster     qtest_add_func("device/introspect/none", test_device_intro_none);
3282d1abb85SMarkus Armbruster     qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
3291c6d75d5SEduardo Habkost     qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces);
330410573aaSThomas Huth     if (g_test_quick()) {
331410573aaSThomas Huth         qtest_add_data_func("device/introspect/concrete/defaults/none",
332eefd26b8SAkihiko Odaki                             common_args, test_device_intro_concrete);
333410573aaSThomas Huth     } else {
334410573aaSThomas Huth         qtest_cb_for_every_machine(add_machine_test_case, true);
335410573aaSThomas Huth     }
3362d1abb85SMarkus Armbruster 
3372d1abb85SMarkus Armbruster     return g_test_run();
3382d1abb85SMarkus Armbruster }
339