qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

qmp-cmd-test.c (11285B)


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