qemu/tests/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_VNC
  35        { "query-vnc", ERROR_CLASS_GENERIC_ERROR },
  36        { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR },
  37#endif
  38#ifndef CONFIG_REPLICATION
  39        { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND },
  40#endif
  41        /* Likewise, and require special QEMU command-line arguments: */
  42        { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR },
  43        { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE },
  44        { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR },
  45        { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR },
  46        { NULL, -1 }
  47    };
  48    int i;
  49
  50    for (i = 0; fails[i].cmd; i++) {
  51        if (!strcmp(cmd, fails[i].cmd)) {
  52            return fails[i].err_class;
  53        }
  54    }
  55    return -1;
  56}
  57
  58static void test_query(const void *data)
  59{
  60    const char *cmd = data;
  61    int expected_error_class = query_error_class(cmd);
  62    QDict *resp, *error;
  63    const char *error_class;
  64
  65    qtest_start(common_args);
  66
  67    resp = qmp("{ 'execute': %s }", cmd);
  68    error = qdict_get_qdict(resp, "error");
  69    error_class = error ? qdict_get_str(error, "class") : NULL;
  70
  71    if (expected_error_class < 0) {
  72        g_assert(qdict_haskey(resp, "return"));
  73    } else {
  74        g_assert(error);
  75        g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class,
  76                                        -1, &error_abort),
  77                        ==, expected_error_class);
  78    }
  79    qobject_unref(resp);
  80
  81    qtest_end();
  82}
  83
  84static bool query_is_blacklisted(const char *cmd)
  85{
  86    const char *blacklist[] = {
  87        /* Not actually queries: */
  88        "add-fd",
  89        /* Success depends on target arch: */
  90        "query-cpu-definitions",  /* arm, i386, ppc, s390x */
  91        "query-gic-capabilities", /* arm */
  92        /* Success depends on target-specific build configuration: */
  93        "query-pci",              /* CONFIG_PCI */
  94        /* Success depends on launching SEV guest */
  95        "query-sev-launch-measure",
  96        /* Success depends on Host or Hypervisor SEV support */
  97        "query-sev",
  98        "query-sev-capabilities",
  99        NULL
 100    };
 101    int i;
 102
 103    for (i = 0; blacklist[i]; i++) {
 104        if (!strcmp(cmd, blacklist[i])) {
 105            return true;
 106        }
 107    }
 108    return false;
 109}
 110
 111typedef struct {
 112    SchemaInfoList *list;
 113    GHashTable *hash;
 114} QmpSchema;
 115
 116static void qmp_schema_init(QmpSchema *schema)
 117{
 118    QDict *resp;
 119    Visitor *qiv;
 120    SchemaInfoList *tail;
 121
 122    qtest_start(common_args);
 123    resp = qmp("{ 'execute': 'query-qmp-schema' }");
 124
 125    qiv = qobject_input_visitor_new(qdict_get(resp, "return"));
 126    visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort);
 127    visit_free(qiv);
 128
 129    qobject_unref(resp);
 130    qtest_end();
 131
 132    schema->hash = g_hash_table_new(g_str_hash, g_str_equal);
 133
 134    /* Build @schema: hash table mapping entity name to SchemaInfo */
 135    for (tail = schema->list; tail; tail = tail->next) {
 136        g_hash_table_insert(schema->hash, tail->value->name, tail->value);
 137    }
 138}
 139
 140static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name)
 141{
 142    return g_hash_table_lookup(schema->hash, name);
 143}
 144
 145static void qmp_schema_cleanup(QmpSchema *schema)
 146{
 147    qapi_free_SchemaInfoList(schema->list);
 148    g_hash_table_destroy(schema->hash);
 149}
 150
 151static bool object_type_has_mandatory_members(SchemaInfo *type)
 152{
 153    SchemaInfoObjectMemberList *tail;
 154
 155    g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT);
 156
 157    for (tail = type->u.object.members; tail; tail = tail->next) {
 158        if (!tail->value->has_q_default) {
 159            return true;
 160        }
 161    }
 162
 163    return false;
 164}
 165
 166static void add_query_tests(QmpSchema *schema)
 167{
 168    SchemaInfoList *tail;
 169    SchemaInfo *si, *arg_type, *ret_type;
 170    char *test_name;
 171
 172    /* Test the query-like commands */
 173    for (tail = schema->list; tail; tail = tail->next) {
 174        si = tail->value;
 175        if (si->meta_type != SCHEMA_META_TYPE_COMMAND) {
 176            continue;
 177        }
 178
 179        if (query_is_blacklisted(si->name)) {
 180            continue;
 181        }
 182
 183        arg_type = qmp_schema_lookup(schema, si->u.command.arg_type);
 184        if (object_type_has_mandatory_members(arg_type)) {
 185            continue;
 186        }
 187
 188        ret_type = qmp_schema_lookup(schema, si->u.command.ret_type);
 189        if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT
 190            && !ret_type->u.object.members) {
 191            continue;
 192        }
 193
 194        test_name = g_strdup_printf("qmp/%s", si->name);
 195        qtest_add_data_func(test_name, si->name, test_query);
 196        g_free(test_name);
 197    }
 198}
 199
 200static void test_object_add_without_props(void)
 201{
 202    QTestState *qts;
 203    QDict *resp;
 204
 205    qts = qtest_init(common_args);
 206    resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
 207                    " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }");
 208    g_assert_nonnull(resp);
 209    qmp_assert_error_class(resp, "GenericError");
 210    qtest_quit(qts);
 211}
 212
 213int main(int argc, char *argv[])
 214{
 215    QmpSchema schema;
 216    int ret;
 217
 218    g_test_init(&argc, &argv, NULL);
 219
 220    qmp_schema_init(&schema);
 221    add_query_tests(&schema);
 222
 223    qtest_add_func("qmp/object-add-without-props",
 224                   test_object_add_without_props);
 225    /* TODO: add coverage of generic object-add failure modes */
 226
 227    ret = g_test_run();
 228
 229    qmp_schema_cleanup(&schema);
 230    return ret;
 231}
 232