Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
4 changed files
with
219 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.