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 "qapi/qmp/qstring.h" 221c6d75d5SEduardo Habkost #include "qapi/qmp/qdict.h" 2347e6b297SMarkus Armbruster #include "qapi/qmp/qlist.h" 24*907b5105SMarc-André Lureau #include "libqtest.h" 252d1abb85SMarkus Armbruster 262d1abb85SMarkus Armbruster const char common_args[] = "-nodefaults -machine none"; 272d1abb85SMarkus Armbruster 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 */ 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 */ 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 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 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 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 */ 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 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 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 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 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); 269410573aaSThomas Huth g_free((void *)args); 2702d1abb85SMarkus Armbruster } 2712d1abb85SMarkus Armbruster 2721c6d75d5SEduardo Habkost static void test_abstract_interfaces(void) 2731c6d75d5SEduardo Habkost { 2741c6d75d5SEduardo Habkost QList *all_types; 275dbb2a604SEduardo Habkost QListEntry *e; 276f86285c5SEduardo Habkost QDict *index; 27739fb795aSThomas Huth QTestState *qts; 2781c6d75d5SEduardo Habkost 27939fb795aSThomas Huth qts = qtest_init(common_args); 280f86285c5SEduardo Habkost 28139fb795aSThomas Huth all_types = qom_list_types(qts, "interface", true); 282f86285c5SEduardo Habkost index = qom_type_index(all_types); 2831c6d75d5SEduardo Habkost 284dbb2a604SEduardo Habkost QLIST_FOREACH_ENTRY(all_types, e) { 2857dc847ebSMax Reitz QDict *d = qobject_to(QDict, qlist_entry_obj(e)); 286f86285c5SEduardo Habkost const char *name = qdict_get_str(d, "name"); 2871c6d75d5SEduardo Habkost 288f86285c5SEduardo Habkost /* 289f86285c5SEduardo Habkost * qom-list-types implements=interface returns all types 290f86285c5SEduardo Habkost * that implement _any_ interface (not just interface 291f86285c5SEduardo Habkost * types), so skip the ones that don't have "interface" 292f86285c5SEduardo Habkost * on the parent type chain. 293f86285c5SEduardo Habkost */ 294f86285c5SEduardo Habkost if (!qom_has_parent(index, name, "interface")) { 29587467eaeSEduardo Habkost /* Not an interface type */ 29687467eaeSEduardo Habkost continue; 29787467eaeSEduardo Habkost } 2981c6d75d5SEduardo Habkost 29987467eaeSEduardo Habkost g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); 3001c6d75d5SEduardo Habkost } 3011c6d75d5SEduardo Habkost 302cb3e7f08SMarc-André Lureau qobject_unref(all_types); 303cb3e7f08SMarc-André Lureau qobject_unref(index); 30439fb795aSThomas Huth qtest_quit(qts); 3051c6d75d5SEduardo Habkost } 3061c6d75d5SEduardo Habkost 307410573aaSThomas Huth static void add_machine_test_case(const char *mname) 308410573aaSThomas Huth { 309410573aaSThomas Huth char *path, *args; 310410573aaSThomas Huth 311410573aaSThomas Huth path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); 312410573aaSThomas Huth args = g_strdup_printf("-M %s", mname); 313410573aaSThomas Huth qtest_add_data_func(path, args, test_device_intro_concrete); 314410573aaSThomas Huth g_free(path); 315410573aaSThomas Huth 316410573aaSThomas Huth path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); 317410573aaSThomas Huth args = g_strdup_printf("-nodefaults -M %s", mname); 318410573aaSThomas Huth qtest_add_data_func(path, args, test_device_intro_concrete); 319410573aaSThomas Huth g_free(path); 320410573aaSThomas Huth } 321410573aaSThomas Huth 3222d1abb85SMarkus Armbruster int main(int argc, char **argv) 3232d1abb85SMarkus Armbruster { 3242d1abb85SMarkus Armbruster g_test_init(&argc, &argv, NULL); 3252d1abb85SMarkus Armbruster 3262d1abb85SMarkus Armbruster qtest_add_func("device/introspect/list", test_device_intro_list); 32787467eaeSEduardo Habkost qtest_add_func("device/introspect/list-fields", test_qom_list_fields); 3282d1abb85SMarkus Armbruster qtest_add_func("device/introspect/none", test_device_intro_none); 3292d1abb85SMarkus Armbruster qtest_add_func("device/introspect/abstract", test_device_intro_abstract); 3301c6d75d5SEduardo Habkost qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); 331410573aaSThomas Huth if (g_test_quick()) { 332410573aaSThomas Huth qtest_add_data_func("device/introspect/concrete/defaults/none", 333410573aaSThomas Huth g_strdup(common_args), test_device_intro_concrete); 334410573aaSThomas Huth } else { 335410573aaSThomas Huth qtest_cb_for_every_machine(add_machine_test_case, true); 336410573aaSThomas Huth } 3372d1abb85SMarkus Armbruster 3382d1abb85SMarkus Armbruster return g_test_run(); 3392d1abb85SMarkus Armbruster } 340