xref: /qemu/tests/qtest/device-introspect-test.c (revision f86285c571c49e781d3fa46f3bef3835b8e6f393)
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"
212d1abb85SMarkus Armbruster #include "qemu-common.h"
222d1abb85SMarkus Armbruster #include "qapi/qmp/qstring.h"
231c6d75d5SEduardo Habkost #include "qapi/qmp/qbool.h"
241c6d75d5SEduardo Habkost #include "qapi/qmp/qdict.h"
252d1abb85SMarkus Armbruster #include "libqtest.h"
262d1abb85SMarkus Armbruster 
272d1abb85SMarkus Armbruster const char common_args[] = "-nodefaults -machine none";
282d1abb85SMarkus Armbruster 
291c6d75d5SEduardo Habkost static QList *qom_list_types(const char *implements, 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     }
392d1abb85SMarkus Armbruster     resp = qmp("{'execute': 'qom-list-types',"
401c6d75d5SEduardo Habkost                " 'arguments': %p }", args);
412d1abb85SMarkus Armbruster     g_assert(qdict_haskey(resp, "return"));
422d1abb85SMarkus Armbruster     ret = qdict_get_qlist(resp, "return");
432d1abb85SMarkus Armbruster     QINCREF(ret);
442d1abb85SMarkus Armbruster     QDECREF(resp);
452d1abb85SMarkus Armbruster     return ret;
462d1abb85SMarkus Armbruster }
472d1abb85SMarkus Armbruster 
48*f86285c5SEduardo Habkost /* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */
49*f86285c5SEduardo Habkost static QDict *qom_type_index(QList *types)
50*f86285c5SEduardo Habkost {
51*f86285c5SEduardo Habkost     QDict *index = qdict_new();
52*f86285c5SEduardo Habkost     QListEntry *e;
53*f86285c5SEduardo Habkost 
54*f86285c5SEduardo Habkost     QLIST_FOREACH_ENTRY(types, e) {
55*f86285c5SEduardo Habkost         QDict *d = qobject_to_qdict(qlist_entry_obj(e));
56*f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
57*f86285c5SEduardo Habkost         QINCREF(d);
58*f86285c5SEduardo Habkost         qdict_put(index, name, d);
59*f86285c5SEduardo Habkost     }
60*f86285c5SEduardo Habkost     return index;
61*f86285c5SEduardo Habkost }
62*f86285c5SEduardo Habkost 
63*f86285c5SEduardo Habkost /* Check if @parent is present in the parent chain of @type */
64*f86285c5SEduardo Habkost static bool qom_has_parent(QDict *index, const char *type, const char *parent)
65*f86285c5SEduardo Habkost {
66*f86285c5SEduardo Habkost     while (type) {
67*f86285c5SEduardo Habkost         QDict *d = qdict_get_qdict(index, type);
68*f86285c5SEduardo Habkost         const char *p = d && qdict_haskey(d, "parent") ?
69*f86285c5SEduardo Habkost                         qdict_get_str(d, "parent") :
70*f86285c5SEduardo Habkost                         NULL;
71*f86285c5SEduardo Habkost 
72*f86285c5SEduardo Habkost         if (!strcmp(type, parent)) {
73*f86285c5SEduardo Habkost             return true;
74*f86285c5SEduardo Habkost         }
75*f86285c5SEduardo Habkost 
76*f86285c5SEduardo Habkost         type = p;
77*f86285c5SEduardo Habkost     }
78*f86285c5SEduardo Habkost 
79*f86285c5SEduardo Habkost     return false;
80*f86285c5SEduardo Habkost }
81*f86285c5SEduardo Habkost 
82dbb2a604SEduardo Habkost /* Find an entry on a list returned by qom-list-types */
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) {
88dbb2a604SEduardo Habkost         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 
981c6d75d5SEduardo Habkost static QList *device_type_list(bool abstract)
991c6d75d5SEduardo Habkost {
1001c6d75d5SEduardo Habkost     return qom_list_types("device", abstract);
1011c6d75d5SEduardo Habkost }
1021c6d75d5SEduardo Habkost 
1032d1abb85SMarkus Armbruster static void test_one_device(const char *type)
1042d1abb85SMarkus Armbruster {
1052d1abb85SMarkus Armbruster     QDict *resp;
1062d1abb85SMarkus Armbruster     char *help, *qom_tree;
1072d1abb85SMarkus Armbruster 
1082d1abb85SMarkus Armbruster     resp = qmp("{'execute': 'device-list-properties',"
1092d1abb85SMarkus Armbruster                " 'arguments': {'typename': %s}}",
1102d1abb85SMarkus Armbruster                type);
1112d1abb85SMarkus Armbruster     QDECREF(resp);
1122d1abb85SMarkus Armbruster 
1132d1abb85SMarkus Armbruster     help = hmp("device_add \"%s,help\"", type);
1142d1abb85SMarkus Armbruster     g_free(help);
1152d1abb85SMarkus Armbruster 
1162d1abb85SMarkus Armbruster     /*
1172d1abb85SMarkus Armbruster      * Some devices leave dangling pointers in QOM behind.
1182d1abb85SMarkus Armbruster      * "info qom-tree" has a good chance at crashing then
1192d1abb85SMarkus Armbruster      */
1202d1abb85SMarkus Armbruster     qom_tree = hmp("info qom-tree");
1212d1abb85SMarkus Armbruster     g_free(qom_tree);
1222d1abb85SMarkus Armbruster }
1232d1abb85SMarkus Armbruster 
1242d1abb85SMarkus Armbruster static void test_device_intro_list(void)
1252d1abb85SMarkus Armbruster {
1262d1abb85SMarkus Armbruster     QList *types;
1272d1abb85SMarkus Armbruster     char *help;
1282d1abb85SMarkus Armbruster 
1292d1abb85SMarkus Armbruster     qtest_start(common_args);
1302d1abb85SMarkus Armbruster 
1312d1abb85SMarkus Armbruster     types = device_type_list(true);
1322d1abb85SMarkus Armbruster     QDECREF(types);
1332d1abb85SMarkus Armbruster 
1342d1abb85SMarkus Armbruster     help = hmp("device_add help");
1352d1abb85SMarkus Armbruster     g_free(help);
1362d1abb85SMarkus Armbruster 
1372d1abb85SMarkus Armbruster     qtest_end();
1382d1abb85SMarkus Armbruster }
1392d1abb85SMarkus Armbruster 
140*f86285c5SEduardo Habkost /*
141*f86285c5SEduardo Habkost  * Ensure all entries returned by qom-list-types implements=<parent>
142*f86285c5SEduardo Habkost  * have <parent> as a parent.
143*f86285c5SEduardo Habkost  */
144*f86285c5SEduardo Habkost static void test_qom_list_parents(const char *parent)
145*f86285c5SEduardo Habkost {
146*f86285c5SEduardo Habkost     QList *types;
147*f86285c5SEduardo Habkost     QListEntry *e;
148*f86285c5SEduardo Habkost     QDict *index;
149*f86285c5SEduardo Habkost 
150*f86285c5SEduardo Habkost     types = qom_list_types(parent, true);
151*f86285c5SEduardo Habkost     index = qom_type_index(types);
152*f86285c5SEduardo Habkost 
153*f86285c5SEduardo Habkost     QLIST_FOREACH_ENTRY(types, e) {
154*f86285c5SEduardo Habkost         QDict *d = qobject_to_qdict(qlist_entry_obj(e));
155*f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
156*f86285c5SEduardo Habkost 
157*f86285c5SEduardo Habkost         g_assert(qom_has_parent(index, name, parent));
158*f86285c5SEduardo Habkost     }
159*f86285c5SEduardo Habkost 
160*f86285c5SEduardo Habkost     QDECREF(types);
161*f86285c5SEduardo Habkost     QDECREF(index);
162*f86285c5SEduardo Habkost }
163*f86285c5SEduardo Habkost 
16487467eaeSEduardo Habkost static void test_qom_list_fields(void)
16587467eaeSEduardo Habkost {
16687467eaeSEduardo Habkost     QList *all_types;
16787467eaeSEduardo Habkost     QList *non_abstract;
16887467eaeSEduardo Habkost     QListEntry *e;
16987467eaeSEduardo Habkost 
17087467eaeSEduardo Habkost     qtest_start(common_args);
17187467eaeSEduardo Habkost 
17287467eaeSEduardo Habkost     all_types = qom_list_types(NULL, true);
17387467eaeSEduardo Habkost     non_abstract = qom_list_types(NULL, false);
17487467eaeSEduardo Habkost 
17587467eaeSEduardo Habkost     QLIST_FOREACH_ENTRY(all_types, e) {
17687467eaeSEduardo Habkost         QDict *d = qobject_to_qdict(qlist_entry_obj(e));
17787467eaeSEduardo Habkost         const char *name = qdict_get_str(d, "name");
17887467eaeSEduardo Habkost         bool abstract = qdict_haskey(d, "abstract") ?
17987467eaeSEduardo Habkost                         qdict_get_bool(d, "abstract") :
18087467eaeSEduardo Habkost                         false;
18187467eaeSEduardo Habkost         bool expected_abstract = !type_list_find(non_abstract, name);
18287467eaeSEduardo Habkost 
18387467eaeSEduardo Habkost         g_assert(abstract == expected_abstract);
18487467eaeSEduardo Habkost     }
18587467eaeSEduardo Habkost 
186*f86285c5SEduardo Habkost     test_qom_list_parents("object");
187*f86285c5SEduardo Habkost     test_qom_list_parents("device");
188*f86285c5SEduardo Habkost     test_qom_list_parents("sys-bus-device");
189*f86285c5SEduardo Habkost 
19087467eaeSEduardo Habkost     QDECREF(all_types);
19187467eaeSEduardo Habkost     QDECREF(non_abstract);
19287467eaeSEduardo Habkost     qtest_end();
19387467eaeSEduardo Habkost }
19487467eaeSEduardo Habkost 
1952d1abb85SMarkus Armbruster static void test_device_intro_none(void)
1962d1abb85SMarkus Armbruster {
1972d1abb85SMarkus Armbruster     qtest_start(common_args);
1982d1abb85SMarkus Armbruster     test_one_device("nonexistent");
1992d1abb85SMarkus Armbruster     qtest_end();
2002d1abb85SMarkus Armbruster }
2012d1abb85SMarkus Armbruster 
2022d1abb85SMarkus Armbruster static void test_device_intro_abstract(void)
2032d1abb85SMarkus Armbruster {
2042d1abb85SMarkus Armbruster     qtest_start(common_args);
2052d1abb85SMarkus Armbruster     test_one_device("device");
2062d1abb85SMarkus Armbruster     qtest_end();
2072d1abb85SMarkus Armbruster }
2082d1abb85SMarkus Armbruster 
2092d1abb85SMarkus Armbruster static void test_device_intro_concrete(void)
2102d1abb85SMarkus Armbruster {
2112d1abb85SMarkus Armbruster     QList *types;
2122d1abb85SMarkus Armbruster     QListEntry *entry;
2132d1abb85SMarkus Armbruster     const char *type;
2142d1abb85SMarkus Armbruster 
2152d1abb85SMarkus Armbruster     qtest_start(common_args);
2162d1abb85SMarkus Armbruster     types = device_type_list(false);
2172d1abb85SMarkus Armbruster 
2182d1abb85SMarkus Armbruster     QLIST_FOREACH_ENTRY(types, entry) {
2192d1abb85SMarkus Armbruster         type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)),
2202d1abb85SMarkus Armbruster                                 "name");
2212d1abb85SMarkus Armbruster         g_assert(type);
2222d1abb85SMarkus Armbruster         test_one_device(type);
2232d1abb85SMarkus Armbruster     }
2242d1abb85SMarkus Armbruster 
2252d1abb85SMarkus Armbruster     QDECREF(types);
2262d1abb85SMarkus Armbruster     qtest_end();
2272d1abb85SMarkus Armbruster }
2282d1abb85SMarkus Armbruster 
2291c6d75d5SEduardo Habkost static void test_abstract_interfaces(void)
2301c6d75d5SEduardo Habkost {
2311c6d75d5SEduardo Habkost     QList *all_types;
232dbb2a604SEduardo Habkost     QListEntry *e;
233*f86285c5SEduardo Habkost     QDict *index;
2341c6d75d5SEduardo Habkost 
2351c6d75d5SEduardo Habkost     qtest_start(common_args);
236*f86285c5SEduardo Habkost 
23787467eaeSEduardo Habkost     all_types = qom_list_types("interface", true);
238*f86285c5SEduardo Habkost     index = qom_type_index(all_types);
2391c6d75d5SEduardo Habkost 
240dbb2a604SEduardo Habkost     QLIST_FOREACH_ENTRY(all_types, e) {
241dbb2a604SEduardo Habkost         QDict *d = qobject_to_qdict(qlist_entry_obj(e));
242*f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
2431c6d75d5SEduardo Habkost 
244*f86285c5SEduardo Habkost         /*
245*f86285c5SEduardo Habkost          * qom-list-types implements=interface returns all types
246*f86285c5SEduardo Habkost          * that implement _any_ interface (not just interface
247*f86285c5SEduardo Habkost          * types), so skip the ones that don't have "interface"
248*f86285c5SEduardo Habkost          * on the parent type chain.
249*f86285c5SEduardo Habkost          */
250*f86285c5SEduardo Habkost         if (!qom_has_parent(index, name, "interface")) {
25187467eaeSEduardo Habkost             /* Not an interface type */
25287467eaeSEduardo Habkost             continue;
25387467eaeSEduardo Habkost         }
2541c6d75d5SEduardo Habkost 
25587467eaeSEduardo Habkost         g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract"));
2561c6d75d5SEduardo Habkost     }
2571c6d75d5SEduardo Habkost 
2581c6d75d5SEduardo Habkost     QDECREF(all_types);
259*f86285c5SEduardo Habkost     QDECREF(index);
2601c6d75d5SEduardo Habkost     qtest_end();
2611c6d75d5SEduardo Habkost }
2621c6d75d5SEduardo Habkost 
2632d1abb85SMarkus Armbruster int main(int argc, char **argv)
2642d1abb85SMarkus Armbruster {
2652d1abb85SMarkus Armbruster     g_test_init(&argc, &argv, NULL);
2662d1abb85SMarkus Armbruster 
2672d1abb85SMarkus Armbruster     qtest_add_func("device/introspect/list", test_device_intro_list);
26887467eaeSEduardo Habkost     qtest_add_func("device/introspect/list-fields", test_qom_list_fields);
2692d1abb85SMarkus Armbruster     qtest_add_func("device/introspect/none", test_device_intro_none);
2702d1abb85SMarkus Armbruster     qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
2712d1abb85SMarkus Armbruster     qtest_add_func("device/introspect/concrete", test_device_intro_concrete);
2721c6d75d5SEduardo Habkost     qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces);
2732d1abb85SMarkus Armbruster 
2742d1abb85SMarkus Armbruster     return g_test_run();
2752d1abb85SMarkus Armbruster }
276