Skip to content

Commit

Permalink
qmp-cmd-test: Split off qmp-test
Browse files Browse the repository at this point in the history
qmp-test is for QMP protocol tests.  Commit e4a426e added generic,
basic tests of query commands to it.  Move them to their own test
program qmp-cmd-test, to keep qmp-test focused on the protocol.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180823164025.12553-6-armbru@redhat.com>
  • Loading branch information
Markus Armbruster committed Aug 24, 2018
1 parent 5365490 commit d93bb9d
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 191 deletions.
1 change: 1 addition & 0 deletions MAINTAINERS
Expand Up @@ -1715,6 +1715,7 @@ F: monitor.c
F: docs/devel/*qmp-*
F: scripts/qmp/
F: tests/qmp-test.c
F: tests/qmp-cmd-test.c
T: git git://repo.or.cz/qemu/armbru.git qapi-next

qtest
Expand Down
3 changes: 3 additions & 0 deletions tests/Makefile.include
Expand Up @@ -183,6 +183,8 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh

check-qtest-generic-y = tests/qmp-test$(EXESUF)
gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c
check-qtest-generic-y += tests/qmp-cmd-test$(EXESUF)

check-qtest-generic-y += tests/device-introspect-test$(EXESUF)
gcov-files-generic-y = qdev-monitor.c qmp.c
check-qtest-generic-y += tests/cdrom-test$(EXESUF)
Expand Down Expand Up @@ -779,6 +781,7 @@ libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o

tests/qmp-test$(EXESUF): tests/qmp-test.o
tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o
tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
Expand Down
213 changes: 213 additions & 0 deletions tests/qmp-cmd-test.c
@@ -0,0 +1,213 @@
/*
* QMP command test cases
*
* Copyright (c) 2017 Red Hat Inc.
*
* Authors:
* Markus Armbruster <armbru@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/

#include "qemu/osdep.h"
#include "libqtest.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-introspect.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qobject-input-visitor.h"

const char common_args[] = "-nodefaults -machine none";

/* Query smoke tests */

static int query_error_class(const char *cmd)
{
static struct {
const char *cmd;
int err_class;
} fails[] = {
/* Success depends on build configuration: */
#ifndef CONFIG_SPICE
{ "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND },
#endif
#ifndef CONFIG_VNC
{ "query-vnc", ERROR_CLASS_GENERIC_ERROR },
{ "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR },
#endif
#ifndef CONFIG_REPLICATION
{ "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND },
#endif
/* Likewise, and require special QEMU command-line arguments: */
{ "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR },
{ "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE },
{ "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR },
{ "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR },
{ NULL, -1 }
};
int i;

for (i = 0; fails[i].cmd; i++) {
if (!strcmp(cmd, fails[i].cmd)) {
return fails[i].err_class;
}
}
return -1;
}

static void test_query(const void *data)
{
const char *cmd = data;
int expected_error_class = query_error_class(cmd);
QDict *resp, *error;
const char *error_class;

qtest_start(common_args);

resp = qmp("{ 'execute': %s }", cmd);
error = qdict_get_qdict(resp, "error");
error_class = error ? qdict_get_str(error, "class") : NULL;

if (expected_error_class < 0) {
g_assert(qdict_haskey(resp, "return"));
} else {
g_assert(error);
g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class,
-1, &error_abort),
==, expected_error_class);
}
qobject_unref(resp);

qtest_end();
}

static bool query_is_blacklisted(const char *cmd)
{
const char *blacklist[] = {
/* Not actually queries: */
"add-fd",
/* Success depends on target arch: */
"query-cpu-definitions", /* arm, i386, ppc, s390x */
"query-gic-capabilities", /* arm */
/* Success depends on target-specific build configuration: */
"query-pci", /* CONFIG_PCI */
/* Success depends on launching SEV guest */
"query-sev-launch-measure",
/* Success depends on Host or Hypervisor SEV support */
"query-sev",
"query-sev-capabilities",
NULL
};
int i;

for (i = 0; blacklist[i]; i++) {
if (!strcmp(cmd, blacklist[i])) {
return true;
}
}
return false;
}

typedef struct {
SchemaInfoList *list;
GHashTable *hash;
} QmpSchema;

static void qmp_schema_init(QmpSchema *schema)
{
QDict *resp;
Visitor *qiv;
SchemaInfoList *tail;

qtest_start(common_args);
resp = qmp("{ 'execute': 'query-qmp-schema' }");

qiv = qobject_input_visitor_new(qdict_get(resp, "return"));
visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort);
visit_free(qiv);

qobject_unref(resp);
qtest_end();

schema->hash = g_hash_table_new(g_str_hash, g_str_equal);

/* Build @schema: hash table mapping entity name to SchemaInfo */
for (tail = schema->list; tail; tail = tail->next) {
g_hash_table_insert(schema->hash, tail->value->name, tail->value);
}
}

static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name)
{
return g_hash_table_lookup(schema->hash, name);
}

static void qmp_schema_cleanup(QmpSchema *schema)
{
qapi_free_SchemaInfoList(schema->list);
g_hash_table_destroy(schema->hash);
}

static bool object_type_has_mandatory_members(SchemaInfo *type)
{
SchemaInfoObjectMemberList *tail;

g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT);

for (tail = type->u.object.members; tail; tail = tail->next) {
if (!tail->value->has_q_default) {
return true;
}
}

return false;
}

static void add_query_tests(QmpSchema *schema)
{
SchemaInfoList *tail;
SchemaInfo *si, *arg_type, *ret_type;
char *test_name;

/* Test the query-like commands */
for (tail = schema->list; tail; tail = tail->next) {
si = tail->value;
if (si->meta_type != SCHEMA_META_TYPE_COMMAND) {
continue;
}

if (query_is_blacklisted(si->name)) {
continue;
}

arg_type = qmp_schema_lookup(schema, si->u.command.arg_type);
if (object_type_has_mandatory_members(arg_type)) {
continue;
}

ret_type = qmp_schema_lookup(schema, si->u.command.ret_type);
if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT
&& !ret_type->u.object.members) {
continue;
}

test_name = g_strdup_printf("qmp/%s", si->name);
qtest_add_data_func(test_name, si->name, test_query);
g_free(test_name);
}
}

int main(int argc, char *argv[])
{
QmpSchema schema;
int ret;

g_test_init(&argc, &argv, NULL);

qmp_schema_init(&schema);
add_query_tests(&schema);
ret = g_test_run();

qmp_schema_cleanup(&schema);
return ret;
}

0 comments on commit d93bb9d

Please sign in to comment.