qemu/tests/qtest/qmp-cmd-test.c
<<
>>
Prefs
   1/*
   2 * QMP command test cases
   3 *
   4 * Copyright (c) 2017 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#include "qemu/osdep.h"
  14#include "libqos/libqtest.h"
  15#include "qapi/error.h"
  16#include "qapi/qapi-visit-introspect.h"
  17#include "qapi/qmp/qdict.h"
  18#include "qapi/qobject-input-visitor.h"
  19
  20const char common_args[] = "-nodefaults -machine none";
  21
  22/* Query smoke tests */
  23
  24static int query_error_class(const char *cmd)
  25{
  26    static struct {
  27        const char *cmd;
  28        int err_class;
  29    } fails[] = {
  30        /* Success depends on build configuration: */
  31#ifndef CONFIG_SPICE
  32        { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND },
  33#endif
  34#ifndef CONFIG_TCG
  35        { "query-replay", ERROR_CLASS_COMMAND_NOT_FOUND },
  36#endif
  37#ifndef CONFIG_VNC
  38        { "query-vnc", ERROR_CLASS_GENERIC_ERROR },
  39        { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR },
  40#endif
  41#ifndef CONFIG_REPLICATION
  42        { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND },
  43#endif
  44        /* Likewise, and require special QEMU command-line arguments: */
  45        { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR },
  46        { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE },
  47        { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR },
  48        { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR },
  49#ifndef CONFIG_PROFILER
  50        { "x-query-profile", ERROR_CLASS_GENERIC_ERROR },
  51#endif
  52        /* Only valid with a USB bus added */
  53        { "x-query-usb", ERROR_CLASS_GENERIC_ERROR },
  54        /* Only valid with accel=tcg */
  55        { "x-query-jit", ERROR_CLASS_GENERIC_ERROR },
  56        { "x-query-opcount", ERROR_CLASS_GENERIC_ERROR },
  57        { NULL, -1 }
  58    };
  59    int i;
  60
  61    for (i = 0; fails[i].cmd; i++) {
  62        if (!strcmp(cmd, fails[i].cmd)) {
  63            return fails[i].err_class;
  64        }
  65    }
  66    return -1;
  67}
  68
  69static void test_query(const void *data)
  70{
  71    const char *cmd = data;
  72    int expected_error_class = query_error_class(cmd);
  73    QDict *resp, *error;
  74    const char *error_class;
  75    QTestState *qts;
  76
  77    qts = qtest_init(common_args);
  78
  79    resp = qtest_qmp(qts, "{ 'execute': %s }", cmd);
  80    error = qdict_get_qdict(resp, "error");
  81    error_class = error ? qdict_get_str(error, "class") : NULL;
  82
  83    if (expected_error_class < 0) {
  84        g_assert(qdict_haskey(resp, "return"));
  85    } else {
  86        g_assert(error);
  87        g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class,
  88                                        -1, &error_abort),
  89                        ==, expected_error_class);
  90    }
  91    qobject_unref(resp);
  92
  93    qtest_quit(qts);
  94}
  95
  96static bool query_is_ignored(const char *cmd)
  97{
  98    const char *ignored[] = {
  99        /* Not actually queries: */
 100        "add-fd",
 101        /* Success depends on target arch: */
 102        "query-cpu-definitions",  /* arm, i386, ppc, s390x */
 103        "query-gic-capabilities", /* arm */
 104        /* Success depends on target-specific build configuration: */
 105        "query-pci",              /* CONFIG_PCI */
 106        /* Success depends on launching SEV guest */
 107        "query-sev-launch-measure",
 108        /* Success depends on Host or Hypervisor SEV support */
 109        "query-sev",
 110        "query-sev-capabilities",
 111        "query-sgx",
 112        "query-sgx-capabilities",
 113        NULL
 114    };
 115    int i;
 116
 117    for (i = 0; ignored[i]; i++) {
 118        if (!strcmp(cmd, ignored[i])) {
 119            return true;
 120        }
 121    }
 122    return false;
 123}
 124
 125typedef struct {
 126    SchemaInfoList *list;
 127    GHashTable *hash;
 128} QmpSchema;
 129
 130static void qmp_schema_init(QmpSchema *schema)
 131{
 132    QDict *resp;
 133    Visitor *qiv;
 134    SchemaInfoList *tail;
 135    QTestState *qts;
 136
 137    qts = qtest_init(common_args);
 138
 139    resp = qtest_qmp(qts, "{ 'execute': 'query-qmp-schema' }");
 140
 141    qiv = qobject_input_visitor_new(qdict_get(resp, "return"));
 142    visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort);
 143    visit_free(qiv);
 144
 145    qobject_unref(resp);
 146    qtest_quit(qts);
 147
 148    schema->hash = g_hash_table_new(g_str_hash, g_str_equal);
 149
 150    /* Build @schema: hash table mapping entity name to SchemaInfo */
 151    for (tail = schema->list; tail; tail = tail->next) {
 152        g_hash_table_insert(schema->hash, tail->value->name, tail->value);
 153    }
 154}
 155
 156static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name)
 157{
 158    return g_hash_table_lookup(schema->hash, name);
 159}
 160
 161static void qmp_schema_cleanup(QmpSchema *schema)
 162{
 163    qapi_free_SchemaInfoList(schema->list);
 164    g_hash_table_destroy(schema->hash);
 165}
 166
 167static bool object_type_has_mandatory_members(SchemaInfo *type)
 168{
 169    SchemaInfoObjectMemberList *tail;
 170
 171    g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT);
 172
 173    for (tail = type->u.object.members; tail; tail = tail->next) {
 174        if (!tail->value->has_q_default) {
 175            return true;
 176        }
 177    }
 178
 179    return false;
 180}
 181
 182static void add_query_tests(QmpSchema *schema)
 183{
 184    SchemaInfoList *tail;
 185    SchemaInfo *si, *arg_type, *ret_type;
 186    char *test_name;
 187
 188    /* Test the query-like commands */
 189    for (tail = schema->list; tail; tail = tail->next) {
 190        si = tail->value;
 191        if (si->meta_type != SCHEMA_META_TYPE_COMMAND) {
 192            continue;
 193        }
 194
 195        if (query_is_ignored(si->name)) {
 196            continue;
 197        }
 198
 199        arg_type = qmp_schema_lookup(schema, si->u.command.arg_type);
 200        if (object_type_has_mandatory_members(arg_type)) {
 201            continue;
 202        }
 203
 204        ret_type = qmp_schema_lookup(schema, si->u.command.ret_type);
 205        if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT
 206            && !ret_type->u.object.members) {
 207            continue;
 208        }
 209
 210        test_name = g_strdup_printf("qmp/%s", si->name);
 211        qtest_add_data_func(test_name, si->name, test_query);
 212        g_free(test_name);
 213    }
 214}
 215
 216static void test_object_add_failure_modes(void)
 217{
 218    QTestState *qts;
 219    QDict *resp;
 220
 221    /* attempt to create an object without props */
 222    qts = qtest_init(common_args);
 223    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 224                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }");
 225    g_assert_nonnull(resp);
 226    qmp_expect_error_and_unref(resp, "GenericError");
 227
 228    /* attempt to create an object without qom-type */
 229    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 230                     " {'id': 'ram1' } }");
 231    g_assert_nonnull(resp);
 232    qmp_expect_error_and_unref(resp, "GenericError");
 233
 234    /* attempt to delete an object that does not exist */
 235    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 236                     " {'id': 'ram1' } }");
 237    g_assert_nonnull(resp);
 238    qmp_expect_error_and_unref(resp, "GenericError");
 239
 240    /* attempt to create 2 objects with duplicate id */
 241    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 242                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 243                     " 'size': 1048576 } }");
 244    g_assert_nonnull(resp);
 245    g_assert(qdict_haskey(resp, "return"));
 246    qobject_unref(resp);
 247
 248    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 249                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 250                     " 'size': 1048576 } }");
 251    g_assert_nonnull(resp);
 252    qmp_expect_error_and_unref(resp, "GenericError");
 253
 254    /* delete ram1 object */
 255    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 256                     " {'id': 'ram1' } }");
 257    g_assert_nonnull(resp);
 258    g_assert(qdict_haskey(resp, "return"));
 259    qobject_unref(resp);
 260
 261    /* attempt to create an object with a property of a wrong type */
 262    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 263                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 264                     " 'size': '1048576' } }");
 265    g_assert_nonnull(resp);
 266    /* now do it right */
 267    qmp_expect_error_and_unref(resp, "GenericError");
 268
 269    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 270                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 271                     " 'size': 1048576 } }");
 272    g_assert_nonnull(resp);
 273    g_assert(qdict_haskey(resp, "return"));
 274    qobject_unref(resp);
 275
 276    /* delete ram1 object */
 277    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 278                     " {'id': 'ram1' } }");
 279    g_assert_nonnull(resp);
 280    g_assert(qdict_haskey(resp, "return"));
 281    qobject_unref(resp);
 282
 283    /* attempt to create an object without the id */
 284    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 285                     " {'qom-type': 'memory-backend-ram',"
 286                     " 'size': 1048576 } }");
 287    g_assert_nonnull(resp);
 288    qmp_expect_error_and_unref(resp, "GenericError");
 289
 290    /* now do it right */
 291    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 292                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 293                     " 'size': 1048576 } }");
 294    g_assert_nonnull(resp);
 295    g_assert(qdict_haskey(resp, "return"));
 296    qobject_unref(resp);
 297
 298    /* delete ram1 object */
 299    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 300                     " {'id': 'ram1' } }");
 301    g_assert_nonnull(resp);
 302    g_assert(qdict_haskey(resp, "return"));
 303    qobject_unref(resp);
 304
 305    /* attempt to set a non existing property */
 306    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 307                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 308                     " 'sized': 1048576 } }");
 309    g_assert_nonnull(resp);
 310    qmp_expect_error_and_unref(resp, "GenericError");
 311
 312    /* now do it right */
 313    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 314                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 315                     " 'size': 1048576 } }");
 316    g_assert_nonnull(resp);
 317    g_assert(qdict_haskey(resp, "return"));
 318    qobject_unref(resp);
 319
 320    /* delete ram1 object without id */
 321    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 322                     " {'ida': 'ram1' } }");
 323    g_assert_nonnull(resp);
 324    qobject_unref(resp);
 325
 326    /* delete ram1 object */
 327    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 328                     " {'id': 'ram1' } }");
 329    g_assert_nonnull(resp);
 330    g_assert(qdict_haskey(resp, "return"));
 331    qobject_unref(resp);
 332
 333    /* delete ram1 object that does not exist anymore*/
 334    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 335                     " {'id': 'ram1' } }");
 336    g_assert_nonnull(resp);
 337    qmp_expect_error_and_unref(resp, "GenericError");
 338
 339    qtest_quit(qts);
 340}
 341
 342int main(int argc, char *argv[])
 343{
 344    QmpSchema schema;
 345    int ret;
 346
 347    g_test_init(&argc, &argv, NULL);
 348
 349    qmp_schema_init(&schema);
 350    add_query_tests(&schema);
 351
 352    qtest_add_func("qmp/object-add-failure-modes",
 353                   test_object_add_failure_modes);
 354
 355    ret = g_test_run();
 356
 357    qmp_schema_cleanup(&schema);
 358    return ret;
 359}
 360