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;
 107    char *qom_tree_start, *qom_tree_end;
 108    char *qtree_start, *qtree_end;
 109
 110    g_test_message("Testing device '%s'", type);
 111
 112    qom_tree_start = hmp("info qom-tree");
 113    qtree_start = hmp("info qtree");
 114
 115    resp = qmp("{'execute': 'device-list-properties',"
 116               " 'arguments': {'typename': %s}}",
 117               type);
 118    qobject_unref(resp);
 119
 120    help = hmp("device_add \"%s,help\"", type);
 121    g_free(help);
 122
 123    /*
 124     * Some devices leave dangling pointers in QOM behind.
 125     * "info qom-tree" or "info qtree" have a good chance at crashing then.
 126     * Also make sure that the tree did not change.
 127     */
 128    qom_tree_end = hmp("info qom-tree");
 129    g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
 130    g_free(qom_tree_start);
 131    g_free(qom_tree_end);
 132
 133    qtree_end = hmp("info qtree");
 134    g_assert_cmpstr(qtree_start, ==, qtree_end);
 135    g_free(qtree_start);
 136    g_free(qtree_end);
 137}
 138
 139static void test_device_intro_list(void)
 140{
 141    QList *types;
 142    char *help;
 143
 144    qtest_start(common_args);
 145
 146    types = device_type_list(true);
 147    qobject_unref(types);
 148
 149    help = hmp("device_add help");
 150    g_free(help);
 151
 152    qtest_end();
 153}
 154
 155/*
 156 * Ensure all entries returned by qom-list-types implements=<parent>
 157 * have <parent> as a parent.
 158 */
 159static void test_qom_list_parents(const char *parent)
 160{
 161    QList *types;
 162    QListEntry *e;
 163    QDict *index;
 164
 165    types = qom_list_types(parent, true);
 166    index = qom_type_index(types);
 167
 168    QLIST_FOREACH_ENTRY(types, e) {
 169        QDict *d = qobject_to(QDict, qlist_entry_obj(e));
 170        const char *name = qdict_get_str(d, "name");
 171
 172        g_assert(qom_has_parent(index, name, parent));
 173    }
 174
 175    qobject_unref(types);
 176    qobject_unref(index);
 177}
 178
 179static void test_qom_list_fields(void)
 180{
 181    QList *all_types;
 182    QList *non_abstract;
 183    QListEntry *e;
 184
 185    qtest_start(common_args);
 186
 187    all_types = qom_list_types(NULL, true);
 188    non_abstract = qom_list_types(NULL, false);
 189
 190    QLIST_FOREACH_ENTRY(all_types, e) {
 191        QDict *d = qobject_to(QDict, qlist_entry_obj(e));
 192        const char *name = qdict_get_str(d, "name");
 193        bool abstract = qdict_haskey(d, "abstract") ?
 194                        qdict_get_bool(d, "abstract") :
 195                        false;
 196        bool expected_abstract = !type_list_find(non_abstract, name);
 197
 198        g_assert(abstract == expected_abstract);
 199    }
 200
 201    test_qom_list_parents("object");
 202    test_qom_list_parents("device");
 203    test_qom_list_parents("sys-bus-device");
 204
 205    qobject_unref(all_types);
 206    qobject_unref(non_abstract);
 207    qtest_end();
 208}
 209
 210static void test_device_intro_none(void)
 211{
 212    qtest_start(common_args);
 213    test_one_device("nonexistent");
 214    qtest_end();
 215}
 216
 217static void test_device_intro_abstract(void)
 218{
 219    qtest_start(common_args);
 220    test_one_device("device");
 221    qtest_end();
 222}
 223
 224static void test_device_intro_concrete(const void *args)
 225{
 226    QList *types;
 227    QListEntry *entry;
 228    const char *type;
 229
 230    qtest_start(args);
 231    types = device_type_list(false);
 232
 233    QLIST_FOREACH_ENTRY(types, entry) {
 234        type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)),
 235                                 "name");
 236        g_assert(type);
 237        test_one_device(type);
 238    }
 239
 240    qobject_unref(types);
 241    qtest_end();
 242    g_free((void *)args);
 243}
 244
 245static void test_abstract_interfaces(void)
 246{
 247    QList *all_types;
 248    QListEntry *e;
 249    QDict *index;
 250
 251    qtest_start(common_args);
 252
 253    all_types = qom_list_types("interface", true);
 254    index = qom_type_index(all_types);
 255
 256    QLIST_FOREACH_ENTRY(all_types, e) {
 257        QDict *d = qobject_to(QDict, qlist_entry_obj(e));
 258        const char *name = qdict_get_str(d, "name");
 259
 260        /*
 261         * qom-list-types implements=interface returns all types
 262         * that implement _any_ interface (not just interface
 263         * types), so skip the ones that don't have "interface"
 264         * on the parent type chain.
 265         */
 266        if (!qom_has_parent(index, name, "interface")) {
 267            /* Not an interface type */
 268            continue;
 269        }
 270
 271        g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract"));
 272    }
 273
 274    qobject_unref(all_types);
 275    qobject_unref(index);
 276    qtest_end();
 277}
 278
 279static void add_machine_test_case(const char *mname)
 280{
 281    char *path, *args;
 282
 283    /* Ignore blacklisted machines */
 284    if (g_str_equal("xenfv", mname) || g_str_equal("xenpv", mname)) {
 285        return;
 286    }
 287
 288    path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname);
 289    args = g_strdup_printf("-M %s", mname);
 290    qtest_add_data_func(path, args, test_device_intro_concrete);
 291    g_free(path);
 292
 293    path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname);
 294    args = g_strdup_printf("-nodefaults -M %s", mname);
 295    qtest_add_data_func(path, args, test_device_intro_concrete);
 296    g_free(path);
 297}
 298
 299int main(int argc, char **argv)
 300{
 301    g_test_init(&argc, &argv, NULL);
 302
 303    qtest_add_func("device/introspect/list", test_device_intro_list);
 304    qtest_add_func("device/introspect/list-fields", test_qom_list_fields);
 305    qtest_add_func("device/introspect/none", test_device_intro_none);
 306    qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
 307    qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces);
 308    if (g_test_quick()) {
 309        qtest_add_data_func("device/introspect/concrete/defaults/none",
 310                            g_strdup(common_args), test_device_intro_concrete);
 311    } else {
 312        qtest_cb_for_every_machine(add_machine_test_case, true);
 313    }
 314
 315    return g_test_run();
 316}
 317