qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

device-introspect-test.c (9634B)


      1 /*
      2  * Device introspection test cases
      3  *
      4  * Copyright (c) 2015 Red Hat Inc.
      5  *
      6  * Authors:
      7  *  Markus Armbruster <armbru@redhat.com>,
      8  *
      9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
     10  * See the COPYING file in the top-level directory.
     11  */
     12 
     13 /*
     14  * Covers QMP device-list-properties and HMP device_add help.  We
     15  * currently don't check that their output makes sense, only that QEMU
     16  * survives.  Useful since we've had an astounding number of crash
     17  * bugs around here.
     18  */
     19 
     20 #include "qemu/osdep.h"
     21 #include "qapi/qmp/qstring.h"
     22 #include "qapi/qmp/qdict.h"
     23 #include "qapi/qmp/qlist.h"
     24 #include "libqtest.h"
     25 
     26 const char common_args[] = "-nodefaults -machine none";
     27 
     28 static QList *qom_list_types(QTestState * qts, const char *implements,
     29                              bool abstract)
     30 {
     31     QDict *resp;
     32     QList *ret;
     33     QDict *args = qdict_new();
     34 
     35     qdict_put_bool(args, "abstract", abstract);
     36     if (implements) {
     37         qdict_put_str(args, "implements", implements);
     38     }
     39     resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }",
     40                      args);
     41     g_assert(qdict_haskey(resp, "return"));
     42     ret = qdict_get_qlist(resp, "return");
     43     qobject_ref(ret);
     44     qobject_unref(resp);
     45     return ret;
     46 }
     47 
     48 /* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */
     49 static QDict *qom_type_index(QList *types)
     50 {
     51     QDict *index = qdict_new();
     52     QListEntry *e;
     53 
     54     QLIST_FOREACH_ENTRY(types, e) {
     55         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
     56         const char *name = qdict_get_str(d, "name");
     57         qobject_ref(d);
     58         qdict_put(index, name, d);
     59     }
     60     return index;
     61 }
     62 
     63 /* Check if @parent is present in the parent chain of @type */
     64 static bool qom_has_parent(QDict *index, const char *type, const char *parent)
     65 {
     66     while (type) {
     67         QDict *d = qdict_get_qdict(index, type);
     68         const char *p = d && qdict_haskey(d, "parent") ?
     69                         qdict_get_str(d, "parent") :
     70                         NULL;
     71 
     72         if (!strcmp(type, parent)) {
     73             return true;
     74         }
     75 
     76         type = p;
     77     }
     78 
     79     return false;
     80 }
     81 
     82 /* Find an entry on a list returned by qom-list-types */
     83 static QDict *type_list_find(QList *types, const char *name)
     84 {
     85     QListEntry *e;
     86 
     87     QLIST_FOREACH_ENTRY(types, e) {
     88         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
     89         const char *ename = qdict_get_str(d, "name");
     90         if (!strcmp(ename, name)) {
     91             return d;
     92         }
     93     }
     94 
     95     return NULL;
     96 }
     97 
     98 static QList *device_type_list(QTestState *qts, bool abstract)
     99 {
    100     return qom_list_types(qts, "device", abstract);
    101 }
    102 
    103 static void test_one_device(QTestState *qts, const char *type)
    104 {
    105     QDict *resp;
    106     char *help, *escaped;
    107     GRegex *comma;
    108 
    109     g_test_message("Testing device '%s'", type);
    110 
    111     resp = qtest_qmp(qts, "{'execute': 'device-list-properties',"
    112                           " 'arguments': {'typename': %s}}",
    113                type);
    114     qobject_unref(resp);
    115 
    116     comma = g_regex_new(",", 0, 0, NULL);
    117     escaped = g_regex_replace_literal(comma, type, -1, 0, ",,", 0, NULL);
    118     g_regex_unref(comma);
    119 
    120     help = qtest_hmp(qts, "device_add \"%s,help\"", escaped);
    121     g_free(help);
    122     g_free(escaped);
    123 }
    124 
    125 static void test_device_intro_list(void)
    126 {
    127     QList *types;
    128     char *help;
    129     QTestState *qts;
    130 
    131     qts = qtest_init(common_args);
    132 
    133     types = device_type_list(qts, true);
    134     qobject_unref(types);
    135 
    136     help = qtest_hmp(qts, "device_add help");
    137     g_free(help);
    138 
    139     qtest_quit(qts);
    140 }
    141 
    142 /*
    143  * Ensure all entries returned by qom-list-types implements=<parent>
    144  * have <parent> as a parent.
    145  */
    146 static void test_qom_list_parents(QTestState *qts, const char *parent)
    147 {
    148     QList *types;
    149     QListEntry *e;
    150     QDict *index;
    151 
    152     types = qom_list_types(qts, parent, true);
    153     index = qom_type_index(types);
    154 
    155     QLIST_FOREACH_ENTRY(types, e) {
    156         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
    157         const char *name = qdict_get_str(d, "name");
    158 
    159         g_assert(qom_has_parent(index, name, parent));
    160     }
    161 
    162     qobject_unref(types);
    163     qobject_unref(index);
    164 }
    165 
    166 static void test_qom_list_fields(void)
    167 {
    168     QList *all_types;
    169     QList *non_abstract;
    170     QListEntry *e;
    171     QTestState *qts;
    172 
    173     qts = qtest_init(common_args);
    174 
    175     all_types = qom_list_types(qts, NULL, true);
    176     non_abstract = qom_list_types(qts, NULL, false);
    177 
    178     QLIST_FOREACH_ENTRY(all_types, e) {
    179         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
    180         const char *name = qdict_get_str(d, "name");
    181         bool abstract = qdict_haskey(d, "abstract") ?
    182                         qdict_get_bool(d, "abstract") :
    183                         false;
    184         bool expected_abstract = !type_list_find(non_abstract, name);
    185 
    186         g_assert(abstract == expected_abstract);
    187     }
    188 
    189     test_qom_list_parents(qts, "object");
    190     test_qom_list_parents(qts, "device");
    191     test_qom_list_parents(qts, "sys-bus-device");
    192 
    193     qobject_unref(all_types);
    194     qobject_unref(non_abstract);
    195     qtest_quit(qts);
    196 }
    197 
    198 static void test_device_intro_none(void)
    199 {
    200     QTestState *qts = qtest_init(common_args);
    201     g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
    202     g_autofree char *qom_tree_end = NULL;
    203     g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
    204     g_autofree char *qtree_end = NULL;
    205 
    206     test_one_device(qts, "nonexistent");
    207 
    208     /* Make sure that really nothing changed in the trees */
    209     qom_tree_end = qtest_hmp(qts, "info qom-tree");
    210     g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
    211     qtree_end = qtest_hmp(qts, "info qtree");
    212     g_assert_cmpstr(qtree_start, ==, qtree_end);
    213 
    214     qtest_quit(qts);
    215 }
    216 
    217 static void test_device_intro_abstract(void)
    218 {
    219     QTestState *qts = qtest_init(common_args);
    220     g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
    221     g_autofree char *qom_tree_end = NULL;
    222     g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
    223     g_autofree char *qtree_end = NULL;
    224 
    225     test_one_device(qts, "device");
    226 
    227     /* Make sure that really nothing changed in the trees */
    228     qom_tree_end = qtest_hmp(qts, "info qom-tree");
    229     g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
    230     qtree_end = qtest_hmp(qts, "info qtree");
    231     g_assert_cmpstr(qtree_start, ==, qtree_end);
    232 
    233     qtest_quit(qts);
    234 }
    235 
    236 static void test_device_intro_concrete(const void *args)
    237 {
    238     QList *types;
    239     QListEntry *entry;
    240     const char *type;
    241     QTestState *qts = qtest_init(args);
    242     g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
    243     g_autofree char *qom_tree_end = NULL;
    244     g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
    245     g_autofree char *qtree_end = NULL;
    246 
    247     types = device_type_list(qts, false);
    248 
    249     QLIST_FOREACH_ENTRY(types, entry) {
    250         type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)),
    251                                  "name");
    252         g_assert(type);
    253         test_one_device(qts, type);
    254     }
    255 
    256     /*
    257      * Some devices leave dangling pointers in QOM behind.
    258      * "info qom-tree" or "info qtree" have a good chance at crashing then.
    259      * Also make sure that the tree did not change.
    260      */
    261     qom_tree_end = qtest_hmp(qts, "info qom-tree");
    262     g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
    263 
    264     qtree_end = qtest_hmp(qts, "info qtree");
    265     g_assert_cmpstr(qtree_start, ==, qtree_end);
    266 
    267     qobject_unref(types);
    268     qtest_quit(qts);
    269     g_free((void *)args);
    270 }
    271 
    272 static void test_abstract_interfaces(void)
    273 {
    274     QList *all_types;
    275     QListEntry *e;
    276     QDict *index;
    277     QTestState *qts;
    278 
    279     qts = qtest_init(common_args);
    280 
    281     all_types = qom_list_types(qts, "interface", true);
    282     index = qom_type_index(all_types);
    283 
    284     QLIST_FOREACH_ENTRY(all_types, e) {
    285         QDict *d = qobject_to(QDict, qlist_entry_obj(e));
    286         const char *name = qdict_get_str(d, "name");
    287 
    288         /*
    289          * qom-list-types implements=interface returns all types
    290          * that implement _any_ interface (not just interface
    291          * types), so skip the ones that don't have "interface"
    292          * on the parent type chain.
    293          */
    294         if (!qom_has_parent(index, name, "interface")) {
    295             /* Not an interface type */
    296             continue;
    297         }
    298 
    299         g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract"));
    300     }
    301 
    302     qobject_unref(all_types);
    303     qobject_unref(index);
    304     qtest_quit(qts);
    305 }
    306 
    307 static void add_machine_test_case(const char *mname)
    308 {
    309     char *path, *args;
    310 
    311     path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname);
    312     args = g_strdup_printf("-M %s", mname);
    313     qtest_add_data_func(path, args, test_device_intro_concrete);
    314     g_free(path);
    315 
    316     path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname);
    317     args = g_strdup_printf("-nodefaults -M %s", mname);
    318     qtest_add_data_func(path, args, test_device_intro_concrete);
    319     g_free(path);
    320 }
    321 
    322 int main(int argc, char **argv)
    323 {
    324     g_test_init(&argc, &argv, NULL);
    325 
    326     qtest_add_func("device/introspect/list", test_device_intro_list);
    327     qtest_add_func("device/introspect/list-fields", test_qom_list_fields);
    328     qtest_add_func("device/introspect/none", test_device_intro_none);
    329     qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
    330     qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces);
    331     if (g_test_quick()) {
    332         qtest_add_data_func("device/introspect/concrete/defaults/none",
    333                             g_strdup(common_args), test_device_intro_concrete);
    334     } else {
    335         qtest_cb_for_every_machine(add_machine_test_case, true);
    336     }
    337 
    338     return g_test_run();
    339 }