1f66e7ac8SMarkus Armbruster /* 2f66e7ac8SMarkus Armbruster * QMP protocol test cases 3f66e7ac8SMarkus Armbruster * 4f66e7ac8SMarkus Armbruster * Copyright (c) 2017 Red Hat Inc. 5f66e7ac8SMarkus Armbruster * 6f66e7ac8SMarkus Armbruster * Authors: 7f66e7ac8SMarkus Armbruster * Markus Armbruster <armbru@redhat.com>, 8f66e7ac8SMarkus Armbruster * 9f66e7ac8SMarkus Armbruster * This work is licensed under the terms of the GNU GPL, version 2 or later. 10f66e7ac8SMarkus Armbruster * See the COPYING file in the top-level directory. 11f66e7ac8SMarkus Armbruster */ 12f66e7ac8SMarkus Armbruster 13f66e7ac8SMarkus Armbruster #include "qemu/osdep.h" 14f66e7ac8SMarkus Armbruster #include "libqtest.h" 15f66e7ac8SMarkus Armbruster #include "qapi/error.h" 16112ed241SMarkus Armbruster #include "qapi/qapi-visit-introspect.h" 17112ed241SMarkus Armbruster #include "qapi/qapi-visit-misc.h" 18452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h" 1947e6b297SMarkus Armbruster #include "qapi/qmp/qlist.h" 20f66e7ac8SMarkus Armbruster #include "qapi/qobject-input-visitor.h" 21e4a426e7SMarkus Armbruster #include "qapi/util.h" 22f66e7ac8SMarkus Armbruster #include "qapi/visitor.h" 23f66e7ac8SMarkus Armbruster 24f66e7ac8SMarkus Armbruster const char common_args[] = "-nodefaults -machine none"; 25f66e7ac8SMarkus Armbruster 26f66e7ac8SMarkus Armbruster static const char *get_error_class(QDict *resp) 27f66e7ac8SMarkus Armbruster { 28f66e7ac8SMarkus Armbruster QDict *error = qdict_get_qdict(resp, "error"); 29f66e7ac8SMarkus Armbruster const char *desc = qdict_get_try_str(error, "desc"); 30f66e7ac8SMarkus Armbruster 31f66e7ac8SMarkus Armbruster g_assert(desc); 32f66e7ac8SMarkus Armbruster return error ? qdict_get_try_str(error, "class") : NULL; 33f66e7ac8SMarkus Armbruster } 34f66e7ac8SMarkus Armbruster 35f66e7ac8SMarkus Armbruster static void test_version(QObject *version) 36f66e7ac8SMarkus Armbruster { 37f66e7ac8SMarkus Armbruster Visitor *v; 38f66e7ac8SMarkus Armbruster VersionInfo *vinfo; 39f66e7ac8SMarkus Armbruster 40f66e7ac8SMarkus Armbruster g_assert(version); 41048abb7bSMarkus Armbruster v = qobject_input_visitor_new(version); 42f66e7ac8SMarkus Armbruster visit_type_VersionInfo(v, "version", &vinfo, &error_abort); 43f66e7ac8SMarkus Armbruster qapi_free_VersionInfo(vinfo); 44f66e7ac8SMarkus Armbruster visit_free(v); 45f66e7ac8SMarkus Armbruster } 46f66e7ac8SMarkus Armbruster 476a5c88f5SEric Blake static void test_malformed(QTestState *qts) 48f66e7ac8SMarkus Armbruster { 49f66e7ac8SMarkus Armbruster QDict *resp; 50f66e7ac8SMarkus Armbruster 51f66e7ac8SMarkus Armbruster /* Not even a dictionary */ 526a5c88f5SEric Blake resp = qtest_qmp(qts, "null"); 53f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 54f66e7ac8SMarkus Armbruster QDECREF(resp); 55f66e7ac8SMarkus Armbruster 56f66e7ac8SMarkus Armbruster /* No "execute" key */ 576a5c88f5SEric Blake resp = qtest_qmp(qts, "{}"); 58f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 59f66e7ac8SMarkus Armbruster QDECREF(resp); 60f66e7ac8SMarkus Armbruster 61f66e7ac8SMarkus Armbruster /* "execute" isn't a string */ 626a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': true }"); 63f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 64f66e7ac8SMarkus Armbruster QDECREF(resp); 65f66e7ac8SMarkus Armbruster 66f66e7ac8SMarkus Armbruster /* "arguments" isn't a dictionary */ 676a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); 68f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 69f66e7ac8SMarkus Armbruster QDECREF(resp); 70f66e7ac8SMarkus Armbruster 71f66e7ac8SMarkus Armbruster /* extra key */ 726a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); 73f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 74f66e7ac8SMarkus Armbruster QDECREF(resp); 75f66e7ac8SMarkus Armbruster } 76f66e7ac8SMarkus Armbruster 77f66e7ac8SMarkus Armbruster static void test_qmp_protocol(void) 78f66e7ac8SMarkus Armbruster { 79f66e7ac8SMarkus Armbruster QDict *resp, *q, *ret; 80f66e7ac8SMarkus Armbruster QList *capabilities; 816a5c88f5SEric Blake QTestState *qts; 82f66e7ac8SMarkus Armbruster 836a5c88f5SEric Blake qts = qtest_init_without_qmp_handshake(common_args); 84f66e7ac8SMarkus Armbruster 85f66e7ac8SMarkus Armbruster /* Test greeting */ 866a5c88f5SEric Blake resp = qtest_qmp_receive(qts); 87f66e7ac8SMarkus Armbruster q = qdict_get_qdict(resp, "QMP"); 88f66e7ac8SMarkus Armbruster g_assert(q); 89f66e7ac8SMarkus Armbruster test_version(qdict_get(q, "version")); 90f66e7ac8SMarkus Armbruster capabilities = qdict_get_qlist(q, "capabilities"); 91f66e7ac8SMarkus Armbruster g_assert(capabilities && qlist_empty(capabilities)); 92f66e7ac8SMarkus Armbruster QDECREF(resp); 93f66e7ac8SMarkus Armbruster 94f66e7ac8SMarkus Armbruster /* Test valid command before handshake */ 956a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); 96f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); 97f66e7ac8SMarkus Armbruster QDECREF(resp); 98f66e7ac8SMarkus Armbruster 99f66e7ac8SMarkus Armbruster /* Test malformed commands before handshake */ 1006a5c88f5SEric Blake test_malformed(qts); 101f66e7ac8SMarkus Armbruster 102f66e7ac8SMarkus Armbruster /* Test handshake */ 1036a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); 104f66e7ac8SMarkus Armbruster ret = qdict_get_qdict(resp, "return"); 105f66e7ac8SMarkus Armbruster g_assert(ret && !qdict_size(ret)); 106f66e7ac8SMarkus Armbruster QDECREF(resp); 107f66e7ac8SMarkus Armbruster 108f66e7ac8SMarkus Armbruster /* Test repeated handshake */ 1096a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); 110f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); 111f66e7ac8SMarkus Armbruster QDECREF(resp); 112f66e7ac8SMarkus Armbruster 113f66e7ac8SMarkus Armbruster /* Test valid command */ 1146a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); 115f66e7ac8SMarkus Armbruster test_version(qdict_get(resp, "return")); 116f66e7ac8SMarkus Armbruster QDECREF(resp); 117f66e7ac8SMarkus Armbruster 118f66e7ac8SMarkus Armbruster /* Test malformed commands */ 1196a5c88f5SEric Blake test_malformed(qts); 120f66e7ac8SMarkus Armbruster 121f66e7ac8SMarkus Armbruster /* Test 'id' */ 1226a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }"); 123f66e7ac8SMarkus Armbruster ret = qdict_get_qdict(resp, "return"); 124f66e7ac8SMarkus Armbruster g_assert(ret); 125f66e7ac8SMarkus Armbruster g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); 126f66e7ac8SMarkus Armbruster QDECREF(resp); 127f66e7ac8SMarkus Armbruster 128f66e7ac8SMarkus Armbruster /* Test command failure with 'id' */ 1296a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); 130f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 131f66e7ac8SMarkus Armbruster g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); 132f66e7ac8SMarkus Armbruster QDECREF(resp); 133f66e7ac8SMarkus Armbruster 1346a5c88f5SEric Blake qtest_quit(qts); 135f66e7ac8SMarkus Armbruster } 136f66e7ac8SMarkus Armbruster 137e4a426e7SMarkus Armbruster static int query_error_class(const char *cmd) 138e4a426e7SMarkus Armbruster { 139e4a426e7SMarkus Armbruster static struct { 140e4a426e7SMarkus Armbruster const char *cmd; 141e4a426e7SMarkus Armbruster int err_class; 142e4a426e7SMarkus Armbruster } fails[] = { 143e4a426e7SMarkus Armbruster /* Success depends on build configuration: */ 144e4a426e7SMarkus Armbruster #ifndef CONFIG_SPICE 145e4a426e7SMarkus Armbruster { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, 146e4a426e7SMarkus Armbruster #endif 147e4a426e7SMarkus Armbruster #ifndef CONFIG_VNC 148e4a426e7SMarkus Armbruster { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, 149e4a426e7SMarkus Armbruster { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, 150e4a426e7SMarkus Armbruster #endif 151e4a426e7SMarkus Armbruster #ifndef CONFIG_REPLICATION 152e4a426e7SMarkus Armbruster { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, 153e4a426e7SMarkus Armbruster #endif 154e4a426e7SMarkus Armbruster /* Likewise, and require special QEMU command-line arguments: */ 155e4a426e7SMarkus Armbruster { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, 156e4a426e7SMarkus Armbruster { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, 157e4a426e7SMarkus Armbruster { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, 158e4a426e7SMarkus Armbruster { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, 159e4a426e7SMarkus Armbruster { NULL, -1 } 160e4a426e7SMarkus Armbruster }; 161e4a426e7SMarkus Armbruster int i; 162e4a426e7SMarkus Armbruster 163e4a426e7SMarkus Armbruster for (i = 0; fails[i].cmd; i++) { 164e4a426e7SMarkus Armbruster if (!strcmp(cmd, fails[i].cmd)) { 165e4a426e7SMarkus Armbruster return fails[i].err_class; 166e4a426e7SMarkus Armbruster } 167e4a426e7SMarkus Armbruster } 168e4a426e7SMarkus Armbruster return -1; 169e4a426e7SMarkus Armbruster } 170e4a426e7SMarkus Armbruster 171e4a426e7SMarkus Armbruster static void test_query(const void *data) 172e4a426e7SMarkus Armbruster { 173e4a426e7SMarkus Armbruster const char *cmd = data; 174e4a426e7SMarkus Armbruster int expected_error_class = query_error_class(cmd); 175e4a426e7SMarkus Armbruster QDict *resp, *error; 176e4a426e7SMarkus Armbruster const char *error_class; 177e4a426e7SMarkus Armbruster 178e4a426e7SMarkus Armbruster qtest_start(common_args); 179e4a426e7SMarkus Armbruster 180e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': %s }", cmd); 181e4a426e7SMarkus Armbruster error = qdict_get_qdict(resp, "error"); 182e4a426e7SMarkus Armbruster error_class = error ? qdict_get_str(error, "class") : NULL; 183e4a426e7SMarkus Armbruster 184e4a426e7SMarkus Armbruster if (expected_error_class < 0) { 185e4a426e7SMarkus Armbruster g_assert(qdict_haskey(resp, "return")); 186e4a426e7SMarkus Armbruster } else { 187e4a426e7SMarkus Armbruster g_assert(error); 188f7abe0ecSMarc-André Lureau g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, 18906c60b6cSMarkus Armbruster -1, &error_abort), 190e4a426e7SMarkus Armbruster ==, expected_error_class); 191e4a426e7SMarkus Armbruster } 192e4a426e7SMarkus Armbruster QDECREF(resp); 193e4a426e7SMarkus Armbruster 194e4a426e7SMarkus Armbruster qtest_end(); 195e4a426e7SMarkus Armbruster } 196e4a426e7SMarkus Armbruster 197e4a426e7SMarkus Armbruster static bool query_is_blacklisted(const char *cmd) 198e4a426e7SMarkus Armbruster { 199e4a426e7SMarkus Armbruster const char *blacklist[] = { 200e4a426e7SMarkus Armbruster /* Not actually queries: */ 201e4a426e7SMarkus Armbruster "add-fd", 202e4a426e7SMarkus Armbruster /* Success depends on target arch: */ 203e4a426e7SMarkus Armbruster "query-cpu-definitions", /* arm, i386, ppc, s390x */ 204e4a426e7SMarkus Armbruster "query-gic-capabilities", /* arm */ 205e4a426e7SMarkus Armbruster /* Success depends on target-specific build configuration: */ 206e4a426e7SMarkus Armbruster "query-pci", /* CONFIG_PCI */ 207*1b6a034fSBrijesh Singh /* Success depends on launching SEV guest */ 208*1b6a034fSBrijesh Singh "query-sev-launch-measure", 20908a161fdSBrijesh Singh /* Success depends on Host or Hypervisor SEV support */ 21008a161fdSBrijesh Singh "query-sev", 211e4a426e7SMarkus Armbruster NULL 212e4a426e7SMarkus Armbruster }; 213e4a426e7SMarkus Armbruster int i; 214e4a426e7SMarkus Armbruster 215e4a426e7SMarkus Armbruster for (i = 0; blacklist[i]; i++) { 216e4a426e7SMarkus Armbruster if (!strcmp(cmd, blacklist[i])) { 217e4a426e7SMarkus Armbruster return true; 218e4a426e7SMarkus Armbruster } 219e4a426e7SMarkus Armbruster } 220e4a426e7SMarkus Armbruster return false; 221e4a426e7SMarkus Armbruster } 222e4a426e7SMarkus Armbruster 223e4a426e7SMarkus Armbruster typedef struct { 224e4a426e7SMarkus Armbruster SchemaInfoList *list; 225e4a426e7SMarkus Armbruster GHashTable *hash; 226e4a426e7SMarkus Armbruster } QmpSchema; 227e4a426e7SMarkus Armbruster 228e4a426e7SMarkus Armbruster static void qmp_schema_init(QmpSchema *schema) 229e4a426e7SMarkus Armbruster { 230e4a426e7SMarkus Armbruster QDict *resp; 231e4a426e7SMarkus Armbruster Visitor *qiv; 232e4a426e7SMarkus Armbruster SchemaInfoList *tail; 233e4a426e7SMarkus Armbruster 234e4a426e7SMarkus Armbruster qtest_start(common_args); 235e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': 'query-qmp-schema' }"); 236e4a426e7SMarkus Armbruster 237e4a426e7SMarkus Armbruster qiv = qobject_input_visitor_new(qdict_get(resp, "return")); 238e4a426e7SMarkus Armbruster visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); 239e4a426e7SMarkus Armbruster visit_free(qiv); 240e4a426e7SMarkus Armbruster 241e4a426e7SMarkus Armbruster QDECREF(resp); 242e4a426e7SMarkus Armbruster qtest_end(); 243e4a426e7SMarkus Armbruster 244e4a426e7SMarkus Armbruster schema->hash = g_hash_table_new(g_str_hash, g_str_equal); 245e4a426e7SMarkus Armbruster 246e4a426e7SMarkus Armbruster /* Build @schema: hash table mapping entity name to SchemaInfo */ 247e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 248e4a426e7SMarkus Armbruster g_hash_table_insert(schema->hash, tail->value->name, tail->value); 249e4a426e7SMarkus Armbruster } 250e4a426e7SMarkus Armbruster } 251e4a426e7SMarkus Armbruster 252e4a426e7SMarkus Armbruster static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) 253e4a426e7SMarkus Armbruster { 254e4a426e7SMarkus Armbruster return g_hash_table_lookup(schema->hash, name); 255e4a426e7SMarkus Armbruster } 256e4a426e7SMarkus Armbruster 257e4a426e7SMarkus Armbruster static void qmp_schema_cleanup(QmpSchema *schema) 258e4a426e7SMarkus Armbruster { 259e4a426e7SMarkus Armbruster qapi_free_SchemaInfoList(schema->list); 260e4a426e7SMarkus Armbruster g_hash_table_destroy(schema->hash); 261e4a426e7SMarkus Armbruster } 262e4a426e7SMarkus Armbruster 263e4a426e7SMarkus Armbruster static bool object_type_has_mandatory_members(SchemaInfo *type) 264e4a426e7SMarkus Armbruster { 265e4a426e7SMarkus Armbruster SchemaInfoObjectMemberList *tail; 266e4a426e7SMarkus Armbruster 267e4a426e7SMarkus Armbruster g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); 268e4a426e7SMarkus Armbruster 269e4a426e7SMarkus Armbruster for (tail = type->u.object.members; tail; tail = tail->next) { 270e4a426e7SMarkus Armbruster if (!tail->value->has_q_default) { 271e4a426e7SMarkus Armbruster return true; 272e4a426e7SMarkus Armbruster } 273e4a426e7SMarkus Armbruster } 274e4a426e7SMarkus Armbruster 275e4a426e7SMarkus Armbruster return false; 276e4a426e7SMarkus Armbruster } 277e4a426e7SMarkus Armbruster 278e4a426e7SMarkus Armbruster static void add_query_tests(QmpSchema *schema) 279e4a426e7SMarkus Armbruster { 280e4a426e7SMarkus Armbruster SchemaInfoList *tail; 281e4a426e7SMarkus Armbruster SchemaInfo *si, *arg_type, *ret_type; 282e313d5ceSMarc-André Lureau char *test_name; 283e4a426e7SMarkus Armbruster 284e4a426e7SMarkus Armbruster /* Test the query-like commands */ 285e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 286e4a426e7SMarkus Armbruster si = tail->value; 287e4a426e7SMarkus Armbruster if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { 288e4a426e7SMarkus Armbruster continue; 289e4a426e7SMarkus Armbruster } 290e4a426e7SMarkus Armbruster 291e4a426e7SMarkus Armbruster if (query_is_blacklisted(si->name)) { 292e4a426e7SMarkus Armbruster continue; 293e4a426e7SMarkus Armbruster } 294e4a426e7SMarkus Armbruster 295e4a426e7SMarkus Armbruster arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); 296e4a426e7SMarkus Armbruster if (object_type_has_mandatory_members(arg_type)) { 297e4a426e7SMarkus Armbruster continue; 298e4a426e7SMarkus Armbruster } 299e4a426e7SMarkus Armbruster 300e4a426e7SMarkus Armbruster ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); 301e4a426e7SMarkus Armbruster if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT 302e4a426e7SMarkus Armbruster && !ret_type->u.object.members) { 303e4a426e7SMarkus Armbruster continue; 304e4a426e7SMarkus Armbruster } 305e4a426e7SMarkus Armbruster 306e4a426e7SMarkus Armbruster test_name = g_strdup_printf("qmp/%s", si->name); 307e4a426e7SMarkus Armbruster qtest_add_data_func(test_name, si->name, test_query); 308e313d5ceSMarc-André Lureau g_free(test_name); 309e4a426e7SMarkus Armbruster } 310e4a426e7SMarkus Armbruster } 311e4a426e7SMarkus Armbruster 312f66e7ac8SMarkus Armbruster int main(int argc, char *argv[]) 313f66e7ac8SMarkus Armbruster { 314e4a426e7SMarkus Armbruster QmpSchema schema; 315e4a426e7SMarkus Armbruster int ret; 316e4a426e7SMarkus Armbruster 317f66e7ac8SMarkus Armbruster g_test_init(&argc, &argv, NULL); 318f66e7ac8SMarkus Armbruster 319f66e7ac8SMarkus Armbruster qtest_add_func("qmp/protocol", test_qmp_protocol); 320e4a426e7SMarkus Armbruster qmp_schema_init(&schema); 321e4a426e7SMarkus Armbruster add_query_tests(&schema); 322f66e7ac8SMarkus Armbruster 323e4a426e7SMarkus Armbruster ret = g_test_run(); 324e4a426e7SMarkus Armbruster 325e4a426e7SMarkus Armbruster qmp_schema_cleanup(&schema); 326e4a426e7SMarkus Armbruster return ret; 327f66e7ac8SMarkus Armbruster } 328