xref: /qemu/tests/qtest/device-introspect-test.c (revision cb3e7f08aeaab0ab13e629ce8496dca150a449ba)
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/qdict.h"
2447e6b297SMarkus Armbruster #include "qapi/qmp/qlist.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");
43*cb3e7f08SMarc-André Lureau     qobject_ref(ret);
44*cb3e7f08SMarc-André Lureau     qobject_unref(resp);
452d1abb85SMarkus Armbruster     return ret;
462d1abb85SMarkus Armbruster }
472d1abb85SMarkus Armbruster 
48f86285c5SEduardo Habkost /* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */
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");
57*cb3e7f08SMarc-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 */
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 */
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 
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);
111*cb3e7f08SMarc-André Lureau     qobject_unref(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);
132*cb3e7f08SMarc-André Lureau     qobject_unref(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 
140f86285c5SEduardo Habkost /*
141f86285c5SEduardo Habkost  * Ensure all entries returned by qom-list-types implements=<parent>
142f86285c5SEduardo Habkost  * have <parent> as a parent.
143f86285c5SEduardo Habkost  */
144f86285c5SEduardo Habkost static void test_qom_list_parents(const char *parent)
145f86285c5SEduardo Habkost {
146f86285c5SEduardo Habkost     QList *types;
147f86285c5SEduardo Habkost     QListEntry *e;
148f86285c5SEduardo Habkost     QDict *index;
149f86285c5SEduardo Habkost 
150f86285c5SEduardo Habkost     types = qom_list_types(parent, true);
151f86285c5SEduardo Habkost     index = qom_type_index(types);
152f86285c5SEduardo Habkost 
153f86285c5SEduardo Habkost     QLIST_FOREACH_ENTRY(types, e) {
1547dc847ebSMax Reitz         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
155f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
156f86285c5SEduardo Habkost 
157f86285c5SEduardo Habkost         g_assert(qom_has_parent(index, name, parent));
158f86285c5SEduardo Habkost     }
159f86285c5SEduardo Habkost 
160*cb3e7f08SMarc-André Lureau     qobject_unref(types);
161*cb3e7f08SMarc-André Lureau     qobject_unref(index);
162f86285c5SEduardo Habkost }
163f86285c5SEduardo 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) {
1767dc847ebSMax Reitz         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 
186f86285c5SEduardo Habkost     test_qom_list_parents("object");
187f86285c5SEduardo Habkost     test_qom_list_parents("device");
188f86285c5SEduardo Habkost     test_qom_list_parents("sys-bus-device");
189f86285c5SEduardo Habkost 
190*cb3e7f08SMarc-André Lureau     qobject_unref(all_types);
191*cb3e7f08SMarc-André Lureau     qobject_unref(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) {
2197dc847ebSMax Reitz         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 
225*cb3e7f08SMarc-André Lureau     qobject_unref(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;
233f86285c5SEduardo Habkost     QDict *index;
2341c6d75d5SEduardo Habkost 
2351c6d75d5SEduardo Habkost     qtest_start(common_args);
236f86285c5SEduardo Habkost 
23787467eaeSEduardo Habkost     all_types = qom_list_types("interface", true);
238f86285c5SEduardo Habkost     index = qom_type_index(all_types);
2391c6d75d5SEduardo Habkost 
240dbb2a604SEduardo Habkost     QLIST_FOREACH_ENTRY(all_types, e) {
2417dc847ebSMax Reitz         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
242f86285c5SEduardo Habkost         const char *name = qdict_get_str(d, "name");
2431c6d75d5SEduardo Habkost 
244f86285c5SEduardo Habkost         /*
245f86285c5SEduardo Habkost          * qom-list-types implements=interface returns all types
246f86285c5SEduardo Habkost          * that implement _any_ interface (not just interface
247f86285c5SEduardo Habkost          * types), so skip the ones that don't have "interface"
248f86285c5SEduardo Habkost          * on the parent type chain.
249f86285c5SEduardo Habkost          */
250f86285c5SEduardo 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 
258*cb3e7f08SMarc-André Lureau     qobject_unref(all_types);
259*cb3e7f08SMarc-André Lureau     qobject_unref(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