qemu/tests/device-introspect-test.c
<<
>>
Prefs
   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 "qemu-common.h"
  22#include "qapi/qmp/qstring.h"
  23#include "qapi/qmp/qdict.h"
  24#include "qapi/qmp/qlist.h"
  25#include "libqtest.h"
  26
  27const char common_args[] = "-nodefaults -machine none";
  28
  29static QList *qom_list_types(const char *implements, 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 = qmp("{'execute': 'qom-list-types',"
  40               " 'arguments': %p }", 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 */
  49static 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 */
  64static 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 */
  83static 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
  98static QList *device_type_list(bool abstract)
  99{
 100    return qom_list_types("device", abstract);
 101}
 102
 103static void test_one_device(const char *type)
 104{
 105    QDict *resp;
 106    char *help, *qom_tree;
 107
 108    resp = qmp("{'execute': 'device-list-properties',"
 109               " 'arguments': {'typename': %s}}",
 110               type);
 111    qobject_unref(resp);
 112
 113    help = hmp("device_add \"%s,help\"", type);
 114    g_free(help);
 115
 116    /*
 117     * Some devices leave dangling pointers in QOM behind.
 118     * "info qom-tree" has a good chance at crashing then
 119     */
 120    qom_tree = hmp("info qom-tree");
 121    g_free(qom_tree);
 122}
 123
 124static void test_device_intro_list(void)
 125{
 126    QList *types;
 127    char *help;
 128
 129    qtest_start(common_args);
 130
 131    types = device_type_list(true);
 132    qobject_unref(types);
 133
 134    help = hmp("device_add help");
 135    g_free(help);
 136
 137    qtest_end();
 138}
 139
 140/*
 141 * Ensure all entries returned by qom-list-types implements=<parent>
 142 * have <parent> as a parent.
 143 */
 144static void test_qom_list_parents(const char *parent)
 145{
 146    QList *types;
 147    QListEntry *e;
 148    QDict *index;
 149
 150    types = qom_list_types(parent, true);
 151    index = qom_type_index(types);
 152
 153    QLIST_FOREACH_ENTRY(types, e) {
 154        QDict *d = qobject_to(QDict, qlist_entry_obj(e));
 155        const char *name = qdict_get_str(d, "name");
 156
 157        g_assert(qom_has_parent(index, name, parent));
 158    }
 159
 160    qobject_unref(types);
 161    qobject_unref(index);
 162}
 163
 164static void test_qom_list_fields(void)
 165{
 166    QList *all_types;
 167    QList *non_abstract;
 168    QListEntry *e;
 169
 170    qtest_start(common_args);
 171
 172    all_types = qom_list_types(NULL, true);
 173    non_abstract = qom_list_types(NULL, false);
 174
 175    QLIST_FOREACH_ENTRY(all_types, e) {
 176        QDict *d = qobject_to(QDict, qlist_entry_obj(e));
 177        const char *name = qdict_get_str(d, "name");
 178        bool abstract = qdict_haskey(d, "abstract") ?
 179                        qdict_get_bool(d, "abstract") :
 180                        false;
 181        bool expected_abstract = !type_list_find(non_abstract, name);
 182
 183        g_assert(abstract == expected_abstract);
 184    }
 185
 186    test_qom_list_parents("object");
 187    test_qom_list_parents("device");
 188    test_qom_list_parents("sys-bus-device");
 189
 190    qobject_unref(all_types);
 191    qobject_unref(non_abstract);
 192    qtest_end();
 193}
 194
 195static void test_device_intro_none(void)
 196{
 197    qtest_start(common_args);
 198    test_one_device("nonexistent");
 199    qtest_end();
 200}
 201
 202static void test_device_intro_abstract(void)
 203{
 204    qtest_start(common_args);
 205    test_one_device("device");
 206    qtest_end();
 207}
 208
 209static void test_device_intro_concrete(void)
 210{
 211    QList *types;
 212    QListEntry *entry;
 213    const char *type;
 214
 215    qtest_start(common_args);
 216    types = device_type_list(false);
 217
 218    QLIST_FOREACH_ENTRY(types, entry) {
 219        type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)),
 220                                 "name");
 221        g_assert(type);
 222        test_one_device(type);
 223    }
 224
 225    qobject_unref(types);
 226    qtest_end();
 227}
 228
 229static void test_abstract_interfaces(void)
 230{
 231    QList *all_types;
 232    QListEntry *e;
 233    QDict *index;
 234
 235    qtest_start(common_args);
 236
 237    all_types = qom_list_types("interface", true);
 238    index = qom_type_index(all_types);
 239
 240    QLIST_FOREACH_ENTRY(all_types, e) {
 241        QDict *d = qobject_to(QDict, qlist_entry_obj(e));
 242        const char *name = qdict_get_str(d, "name");
 243
 244        /*
 245         * qom-list-types implements=interface returns all types
 246         * that implement _any_ interface (not just interface
 247         * types), so skip the ones that don't have "interface"
 248         * on the parent type chain.
 249         */
 250        if (!qom_has_parent(index, name, "interface")) {
 251            /* Not an interface type */
 252            continue;
 253        }
 254
 255        g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract"));
 256    }
 257
 258    qobject_unref(all_types);
 259    qobject_unref(index);
 260    qtest_end();
 261}
 262
 263int main(int argc, char **argv)
 264{
 265    g_test_init(&argc, &argv, NULL);
 266
 267    qtest_add_func("device/introspect/list", test_device_intro_list);
 268    qtest_add_func("device/introspect/list-fields", test_qom_list_fields);
 269    qtest_add_func("device/introspect/none", test_device_intro_none);
 270    qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
 271    qtest_add_func("device/introspect/concrete", test_device_intro_concrete);
 272    qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces);
 273
 274    return g_test_run();
 275}
 276