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