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 "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        /* Success depends on enabling dirty page rate limit */
 114        "query-vcpu-dirty-limit",
 115        NULL
 116    };
 117    int i;
 118
 119    for (i = 0; ignored[i]; i++) {
 120        if (!strcmp(cmd, ignored[i])) {
 121            return true;
 122        }
 123    }
 124    return false;
 125}
 126
 127typedef struct {
 128    SchemaInfoList *list;
 129    GHashTable *hash;
 130} QmpSchema;
 131
 132static void qmp_schema_init(QmpSchema *schema)
 133{
 134    QDict *resp;
 135    Visitor *qiv;
 136    SchemaInfoList *tail;
 137    QTestState *qts;
 138
 139    qts = qtest_init(common_args);
 140
 141    resp = qtest_qmp(qts, "{ 'execute': 'query-qmp-schema' }");
 142
 143    qiv = qobject_input_visitor_new(qdict_get(resp, "return"));
 144    visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort);
 145    visit_free(qiv);
 146
 147    qobject_unref(resp);
 148    qtest_quit(qts);
 149
 150    schema->hash = g_hash_table_new(g_str_hash, g_str_equal);
 151
 152    /* Build @schema: hash table mapping entity name to SchemaInfo */
 153    for (tail = schema->list; tail; tail = tail->next) {
 154        g_hash_table_insert(schema->hash, tail->value->name, tail->value);
 155    }
 156}
 157
 158static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name)
 159{
 160    return g_hash_table_lookup(schema->hash, name);
 161}
 162
 163static void qmp_schema_cleanup(QmpSchema *schema)
 164{
 165    qapi_free_SchemaInfoList(schema->list);
 166    g_hash_table_destroy(schema->hash);
 167}
 168
 169static bool object_type_has_mandatory_members(SchemaInfo *type)
 170{
 171    SchemaInfoObjectMemberList *tail;
 172
 173    g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT);
 174
 175    for (tail = type->u.object.members; tail; tail = tail->next) {
 176        if (!tail->value->has_q_default) {
 177            return true;
 178        }
 179    }
 180
 181    return false;
 182}
 183
 184static void add_query_tests(QmpSchema *schema)
 185{
 186    SchemaInfoList *tail;
 187    SchemaInfo *si, *arg_type, *ret_type;
 188    char *test_name;
 189
 190    /* Test the query-like commands */
 191    for (tail = schema->list; tail; tail = tail->next) {
 192        si = tail->value;
 193        if (si->meta_type != SCHEMA_META_TYPE_COMMAND) {
 194            continue;
 195        }
 196
 197        if (query_is_ignored(si->name)) {
 198            continue;
 199        }
 200
 201        arg_type = qmp_schema_lookup(schema, si->u.command.arg_type);
 202        if (object_type_has_mandatory_members(arg_type)) {
 203            continue;
 204        }
 205
 206        ret_type = qmp_schema_lookup(schema, si->u.command.ret_type);
 207        if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT
 208            && !ret_type->u.object.members) {
 209            continue;
 210        }
 211
 212        test_name = g_strdup_printf("qmp/%s", si->name);
 213        qtest_add_data_func(test_name, si->name, test_query);
 214        g_free(test_name);
 215    }
 216}
 217
 218static void test_object_add_failure_modes(void)
 219{
 220    QTestState *qts;
 221    QDict *resp;
 222
 223    /* attempt to create an object without props */
 224    qts = qtest_init(common_args);
 225    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 226                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }");
 227    g_assert_nonnull(resp);
 228    qmp_expect_error_and_unref(resp, "GenericError");
 229
 230    /* attempt to create an object without qom-type */
 231    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 232                     " {'id': 'ram1' } }");
 233    g_assert_nonnull(resp);
 234    qmp_expect_error_and_unref(resp, "GenericError");
 235
 236    /* attempt to delete an object that does not exist */
 237    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 238                     " {'id': 'ram1' } }");
 239    g_assert_nonnull(resp);
 240    qmp_expect_error_and_unref(resp, "GenericError");
 241
 242    /* attempt to create 2 objects with duplicate id */
 243    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 244                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 245                     " 'size': 1048576 } }");
 246    g_assert_nonnull(resp);
 247    g_assert(qdict_haskey(resp, "return"));
 248    qobject_unref(resp);
 249
 250    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 251                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 252                     " 'size': 1048576 } }");
 253    g_assert_nonnull(resp);
 254    qmp_expect_error_and_unref(resp, "GenericError");
 255
 256    /* delete ram1 object */
 257    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 258                     " {'id': 'ram1' } }");
 259    g_assert_nonnull(resp);
 260    g_assert(qdict_haskey(resp, "return"));
 261    qobject_unref(resp);
 262
 263    /* attempt to create an object with a property of a wrong type */
 264    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 265                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 266                     " 'size': '1048576' } }");
 267    g_assert_nonnull(resp);
 268    /* now do it right */
 269    qmp_expect_error_and_unref(resp, "GenericError");
 270
 271    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 272                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 273                     " 'size': 1048576 } }");
 274    g_assert_nonnull(resp);
 275    g_assert(qdict_haskey(resp, "return"));
 276    qobject_unref(resp);
 277
 278    /* delete ram1 object */
 279    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 280                     " {'id': 'ram1' } }");
 281    g_assert_nonnull(resp);
 282    g_assert(qdict_haskey(resp, "return"));
 283    qobject_unref(resp);
 284
 285    /* attempt to create an object without the id */
 286    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 287                     " {'qom-type': 'memory-backend-ram',"
 288                     " 'size': 1048576 } }");
 289    g_assert_nonnull(resp);
 290    qmp_expect_error_and_unref(resp, "GenericError");
 291
 292    /* now do it right */
 293    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 294                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 295                     " 'size': 1048576 } }");
 296    g_assert_nonnull(resp);
 297    g_assert(qdict_haskey(resp, "return"));
 298    qobject_unref(resp);
 299
 300    /* delete ram1 object */
 301    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 302                     " {'id': 'ram1' } }");
 303    g_assert_nonnull(resp);
 304    g_assert(qdict_haskey(resp, "return"));
 305    qobject_unref(resp);
 306
 307    /* attempt to set a non existing property */
 308    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 309                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 310                     " 'sized': 1048576 } }");
 311    g_assert_nonnull(resp);
 312    qmp_expect_error_and_unref(resp, "GenericError");
 313
 314    /* now do it right */
 315    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 316                     " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
 317                     " 'size': 1048576 } }");
 318    g_assert_nonnull(resp);
 319    g_assert(qdict_haskey(resp, "return"));
 320    qobject_unref(resp);
 321
 322    /* delete ram1 object without id */
 323    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 324                     " {'ida': 'ram1' } }");
 325    g_assert_nonnull(resp);
 326    qobject_unref(resp);
 327
 328    /* delete ram1 object */
 329    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 330                     " {'id': 'ram1' } }");
 331    g_assert_nonnull(resp);
 332    g_assert(qdict_haskey(resp, "return"));
 333    qobject_unref(resp);
 334
 335    /* delete ram1 object that does not exist anymore*/
 336    resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
 337                     " {'id': 'ram1' } }");
 338    g_assert_nonnull(resp);
 339    qmp_expect_error_and_unref(resp, "GenericError");
 340
 341    qtest_quit(qts);
 342}
 343
 344int main(int argc, char *argv[])
 345{
 346    QmpSchema schema;
 347    int ret;
 348
 349    g_test_init(&argc, &argv, NULL);
 350
 351    qmp_schema_init(&schema);
 352    add_query_tests(&schema);
 353
 354    qtest_add_func("qmp/object-add-failure-modes",
 355                   test_object_add_failure_modes);
 356
 357    ret = g_test_run();
 358
 359    qmp_schema_cleanup(&schema);
 360    return ret;
 361}
 362