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-visit.h" 16f66e7ac8SMarkus Armbruster #include "qapi/error.h" 17f66e7ac8SMarkus Armbruster #include "qapi/qobject-input-visitor.h" 18e4a426e7SMarkus Armbruster #include "qapi/util.h" 19f66e7ac8SMarkus Armbruster #include "qapi/visitor.h" 20f66e7ac8SMarkus Armbruster 21f66e7ac8SMarkus Armbruster const char common_args[] = "-nodefaults -machine none"; 22f66e7ac8SMarkus Armbruster 23f66e7ac8SMarkus Armbruster static const char *get_error_class(QDict *resp) 24f66e7ac8SMarkus Armbruster { 25f66e7ac8SMarkus Armbruster QDict *error = qdict_get_qdict(resp, "error"); 26f66e7ac8SMarkus Armbruster const char *desc = qdict_get_try_str(error, "desc"); 27f66e7ac8SMarkus Armbruster 28f66e7ac8SMarkus Armbruster g_assert(desc); 29f66e7ac8SMarkus Armbruster return error ? qdict_get_try_str(error, "class") : NULL; 30f66e7ac8SMarkus Armbruster } 31f66e7ac8SMarkus Armbruster 32f66e7ac8SMarkus Armbruster static void test_version(QObject *version) 33f66e7ac8SMarkus Armbruster { 34f66e7ac8SMarkus Armbruster Visitor *v; 35f66e7ac8SMarkus Armbruster VersionInfo *vinfo; 36f66e7ac8SMarkus Armbruster 37f66e7ac8SMarkus Armbruster g_assert(version); 38048abb7bSMarkus Armbruster v = qobject_input_visitor_new(version); 39f66e7ac8SMarkus Armbruster visit_type_VersionInfo(v, "version", &vinfo, &error_abort); 40f66e7ac8SMarkus Armbruster qapi_free_VersionInfo(vinfo); 41f66e7ac8SMarkus Armbruster visit_free(v); 42f66e7ac8SMarkus Armbruster } 43f66e7ac8SMarkus Armbruster 44f66e7ac8SMarkus Armbruster static void test_malformed(void) 45f66e7ac8SMarkus Armbruster { 46f66e7ac8SMarkus Armbruster QDict *resp; 47f66e7ac8SMarkus Armbruster 48f66e7ac8SMarkus Armbruster /* Not even a dictionary */ 49f66e7ac8SMarkus Armbruster resp = qmp("null"); 50f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 51f66e7ac8SMarkus Armbruster QDECREF(resp); 52f66e7ac8SMarkus Armbruster 53f66e7ac8SMarkus Armbruster /* No "execute" key */ 54f66e7ac8SMarkus Armbruster resp = qmp("{}"); 55f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 56f66e7ac8SMarkus Armbruster QDECREF(resp); 57f66e7ac8SMarkus Armbruster 58f66e7ac8SMarkus Armbruster /* "execute" isn't a string */ 59f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': true }"); 60f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 61f66e7ac8SMarkus Armbruster QDECREF(resp); 62f66e7ac8SMarkus Armbruster 63f66e7ac8SMarkus Armbruster /* "arguments" isn't a dictionary */ 64f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'no-such-cmd', 'arguments': [] }"); 65f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 66f66e7ac8SMarkus Armbruster QDECREF(resp); 67f66e7ac8SMarkus Armbruster 68f66e7ac8SMarkus Armbruster /* extra key */ 69f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'no-such-cmd', 'extra': true }"); 70f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 71f66e7ac8SMarkus Armbruster QDECREF(resp); 72f66e7ac8SMarkus Armbruster } 73f66e7ac8SMarkus Armbruster 74f66e7ac8SMarkus Armbruster static void test_qmp_protocol(void) 75f66e7ac8SMarkus Armbruster { 76f66e7ac8SMarkus Armbruster QDict *resp, *q, *ret; 77f66e7ac8SMarkus Armbruster QList *capabilities; 78f66e7ac8SMarkus Armbruster 79f66e7ac8SMarkus Armbruster global_qtest = qtest_init_without_qmp_handshake(common_args); 80f66e7ac8SMarkus Armbruster 81f66e7ac8SMarkus Armbruster /* Test greeting */ 82f66e7ac8SMarkus Armbruster resp = qmp_receive(); 83f66e7ac8SMarkus Armbruster q = qdict_get_qdict(resp, "QMP"); 84f66e7ac8SMarkus Armbruster g_assert(q); 85f66e7ac8SMarkus Armbruster test_version(qdict_get(q, "version")); 86f66e7ac8SMarkus Armbruster capabilities = qdict_get_qlist(q, "capabilities"); 87f66e7ac8SMarkus Armbruster g_assert(capabilities && qlist_empty(capabilities)); 88f66e7ac8SMarkus Armbruster QDECREF(resp); 89f66e7ac8SMarkus Armbruster 90f66e7ac8SMarkus Armbruster /* Test valid command before handshake */ 91f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'query-version' }"); 92f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); 93f66e7ac8SMarkus Armbruster QDECREF(resp); 94f66e7ac8SMarkus Armbruster 95f66e7ac8SMarkus Armbruster /* Test malformed commands before handshake */ 96f66e7ac8SMarkus Armbruster test_malformed(); 97f66e7ac8SMarkus Armbruster 98f66e7ac8SMarkus Armbruster /* Test handshake */ 99f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'qmp_capabilities' }"); 100f66e7ac8SMarkus Armbruster ret = qdict_get_qdict(resp, "return"); 101f66e7ac8SMarkus Armbruster g_assert(ret && !qdict_size(ret)); 102f66e7ac8SMarkus Armbruster QDECREF(resp); 103f66e7ac8SMarkus Armbruster 104f66e7ac8SMarkus Armbruster /* Test repeated handshake */ 105f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'qmp_capabilities' }"); 106f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); 107f66e7ac8SMarkus Armbruster QDECREF(resp); 108f66e7ac8SMarkus Armbruster 109f66e7ac8SMarkus Armbruster /* Test valid command */ 110f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'query-version' }"); 111f66e7ac8SMarkus Armbruster test_version(qdict_get(resp, "return")); 112f66e7ac8SMarkus Armbruster QDECREF(resp); 113f66e7ac8SMarkus Armbruster 114f66e7ac8SMarkus Armbruster /* Test malformed commands */ 115f66e7ac8SMarkus Armbruster test_malformed(); 116f66e7ac8SMarkus Armbruster 117f66e7ac8SMarkus Armbruster /* Test 'id' */ 118f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'query-name', 'id': 'cookie#1' }"); 119f66e7ac8SMarkus Armbruster ret = qdict_get_qdict(resp, "return"); 120f66e7ac8SMarkus Armbruster g_assert(ret); 121f66e7ac8SMarkus Armbruster g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); 122f66e7ac8SMarkus Armbruster QDECREF(resp); 123f66e7ac8SMarkus Armbruster 124f66e7ac8SMarkus Armbruster /* Test command failure with 'id' */ 125f66e7ac8SMarkus Armbruster resp = qmp("{ 'execute': 'human-monitor-command', 'id': 2 }"); 126f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 127f66e7ac8SMarkus Armbruster g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); 128f66e7ac8SMarkus Armbruster QDECREF(resp); 129f66e7ac8SMarkus Armbruster 130f66e7ac8SMarkus Armbruster qtest_end(); 131f66e7ac8SMarkus Armbruster } 132f66e7ac8SMarkus Armbruster 133e4a426e7SMarkus Armbruster static int query_error_class(const char *cmd) 134e4a426e7SMarkus Armbruster { 135e4a426e7SMarkus Armbruster static struct { 136e4a426e7SMarkus Armbruster const char *cmd; 137e4a426e7SMarkus Armbruster int err_class; 138e4a426e7SMarkus Armbruster } fails[] = { 139e4a426e7SMarkus Armbruster /* Success depends on build configuration: */ 140e4a426e7SMarkus Armbruster #ifndef CONFIG_SPICE 141e4a426e7SMarkus Armbruster { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, 142e4a426e7SMarkus Armbruster #endif 143e4a426e7SMarkus Armbruster #ifndef CONFIG_VNC 144e4a426e7SMarkus Armbruster { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, 145e4a426e7SMarkus Armbruster { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, 146e4a426e7SMarkus Armbruster #endif 147e4a426e7SMarkus Armbruster #ifndef CONFIG_REPLICATION 148e4a426e7SMarkus Armbruster { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, 149e4a426e7SMarkus Armbruster #endif 150e4a426e7SMarkus Armbruster /* Likewise, and require special QEMU command-line arguments: */ 151e4a426e7SMarkus Armbruster { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, 152e4a426e7SMarkus Armbruster { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, 153e4a426e7SMarkus Armbruster { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, 154e4a426e7SMarkus Armbruster { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, 155e4a426e7SMarkus Armbruster { NULL, -1 } 156e4a426e7SMarkus Armbruster }; 157e4a426e7SMarkus Armbruster int i; 158e4a426e7SMarkus Armbruster 159e4a426e7SMarkus Armbruster for (i = 0; fails[i].cmd; i++) { 160e4a426e7SMarkus Armbruster if (!strcmp(cmd, fails[i].cmd)) { 161e4a426e7SMarkus Armbruster return fails[i].err_class; 162e4a426e7SMarkus Armbruster } 163e4a426e7SMarkus Armbruster } 164e4a426e7SMarkus Armbruster return -1; 165e4a426e7SMarkus Armbruster } 166e4a426e7SMarkus Armbruster 167e4a426e7SMarkus Armbruster static void test_query(const void *data) 168e4a426e7SMarkus Armbruster { 169e4a426e7SMarkus Armbruster const char *cmd = data; 170e4a426e7SMarkus Armbruster int expected_error_class = query_error_class(cmd); 171e4a426e7SMarkus Armbruster QDict *resp, *error; 172e4a426e7SMarkus Armbruster const char *error_class; 173e4a426e7SMarkus Armbruster 174e4a426e7SMarkus Armbruster qtest_start(common_args); 175e4a426e7SMarkus Armbruster 176e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': %s }", cmd); 177e4a426e7SMarkus Armbruster error = qdict_get_qdict(resp, "error"); 178e4a426e7SMarkus Armbruster error_class = error ? qdict_get_str(error, "class") : NULL; 179e4a426e7SMarkus Armbruster 180e4a426e7SMarkus Armbruster if (expected_error_class < 0) { 181e4a426e7SMarkus Armbruster g_assert(qdict_haskey(resp, "return")); 182e4a426e7SMarkus Armbruster } else { 183e4a426e7SMarkus Armbruster g_assert(error); 184f7abe0ecSMarc-André Lureau g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, 18506c60b6cSMarkus Armbruster -1, &error_abort), 186e4a426e7SMarkus Armbruster ==, expected_error_class); 187e4a426e7SMarkus Armbruster } 188e4a426e7SMarkus Armbruster QDECREF(resp); 189e4a426e7SMarkus Armbruster 190e4a426e7SMarkus Armbruster qtest_end(); 191e4a426e7SMarkus Armbruster } 192e4a426e7SMarkus Armbruster 193e4a426e7SMarkus Armbruster static bool query_is_blacklisted(const char *cmd) 194e4a426e7SMarkus Armbruster { 195e4a426e7SMarkus Armbruster const char *blacklist[] = { 196e4a426e7SMarkus Armbruster /* Not actually queries: */ 197e4a426e7SMarkus Armbruster "add-fd", 198e4a426e7SMarkus Armbruster /* Success depends on target arch: */ 199e4a426e7SMarkus Armbruster "query-cpu-definitions", /* arm, i386, ppc, s390x */ 200e4a426e7SMarkus Armbruster "query-gic-capabilities", /* arm */ 201e4a426e7SMarkus Armbruster /* Success depends on target-specific build configuration: */ 202e4a426e7SMarkus Armbruster "query-pci", /* CONFIG_PCI */ 203e4a426e7SMarkus Armbruster NULL 204e4a426e7SMarkus Armbruster }; 205e4a426e7SMarkus Armbruster int i; 206e4a426e7SMarkus Armbruster 207e4a426e7SMarkus Armbruster for (i = 0; blacklist[i]; i++) { 208e4a426e7SMarkus Armbruster if (!strcmp(cmd, blacklist[i])) { 209e4a426e7SMarkus Armbruster return true; 210e4a426e7SMarkus Armbruster } 211e4a426e7SMarkus Armbruster } 212e4a426e7SMarkus Armbruster return false; 213e4a426e7SMarkus Armbruster } 214e4a426e7SMarkus Armbruster 215e4a426e7SMarkus Armbruster typedef struct { 216e4a426e7SMarkus Armbruster SchemaInfoList *list; 217e4a426e7SMarkus Armbruster GHashTable *hash; 218e4a426e7SMarkus Armbruster } QmpSchema; 219e4a426e7SMarkus Armbruster 220e4a426e7SMarkus Armbruster static void qmp_schema_init(QmpSchema *schema) 221e4a426e7SMarkus Armbruster { 222e4a426e7SMarkus Armbruster QDict *resp; 223e4a426e7SMarkus Armbruster Visitor *qiv; 224e4a426e7SMarkus Armbruster SchemaInfoList *tail; 225e4a426e7SMarkus Armbruster 226e4a426e7SMarkus Armbruster qtest_start(common_args); 227e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': 'query-qmp-schema' }"); 228e4a426e7SMarkus Armbruster 229e4a426e7SMarkus Armbruster qiv = qobject_input_visitor_new(qdict_get(resp, "return")); 230e4a426e7SMarkus Armbruster visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); 231e4a426e7SMarkus Armbruster visit_free(qiv); 232e4a426e7SMarkus Armbruster 233e4a426e7SMarkus Armbruster QDECREF(resp); 234e4a426e7SMarkus Armbruster qtest_end(); 235e4a426e7SMarkus Armbruster 236e4a426e7SMarkus Armbruster schema->hash = g_hash_table_new(g_str_hash, g_str_equal); 237e4a426e7SMarkus Armbruster 238e4a426e7SMarkus Armbruster /* Build @schema: hash table mapping entity name to SchemaInfo */ 239e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 240e4a426e7SMarkus Armbruster g_hash_table_insert(schema->hash, tail->value->name, tail->value); 241e4a426e7SMarkus Armbruster } 242e4a426e7SMarkus Armbruster } 243e4a426e7SMarkus Armbruster 244e4a426e7SMarkus Armbruster static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) 245e4a426e7SMarkus Armbruster { 246e4a426e7SMarkus Armbruster return g_hash_table_lookup(schema->hash, name); 247e4a426e7SMarkus Armbruster } 248e4a426e7SMarkus Armbruster 249e4a426e7SMarkus Armbruster static void qmp_schema_cleanup(QmpSchema *schema) 250e4a426e7SMarkus Armbruster { 251e4a426e7SMarkus Armbruster qapi_free_SchemaInfoList(schema->list); 252e4a426e7SMarkus Armbruster g_hash_table_destroy(schema->hash); 253e4a426e7SMarkus Armbruster } 254e4a426e7SMarkus Armbruster 255e4a426e7SMarkus Armbruster static bool object_type_has_mandatory_members(SchemaInfo *type) 256e4a426e7SMarkus Armbruster { 257e4a426e7SMarkus Armbruster SchemaInfoObjectMemberList *tail; 258e4a426e7SMarkus Armbruster 259e4a426e7SMarkus Armbruster g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); 260e4a426e7SMarkus Armbruster 261e4a426e7SMarkus Armbruster for (tail = type->u.object.members; tail; tail = tail->next) { 262e4a426e7SMarkus Armbruster if (!tail->value->has_q_default) { 263e4a426e7SMarkus Armbruster return true; 264e4a426e7SMarkus Armbruster } 265e4a426e7SMarkus Armbruster } 266e4a426e7SMarkus Armbruster 267e4a426e7SMarkus Armbruster return false; 268e4a426e7SMarkus Armbruster } 269e4a426e7SMarkus Armbruster 270e4a426e7SMarkus Armbruster static void add_query_tests(QmpSchema *schema) 271e4a426e7SMarkus Armbruster { 272e4a426e7SMarkus Armbruster SchemaInfoList *tail; 273e4a426e7SMarkus Armbruster SchemaInfo *si, *arg_type, *ret_type; 274*e313d5ceSMarc-André Lureau char *test_name; 275e4a426e7SMarkus Armbruster 276e4a426e7SMarkus Armbruster /* Test the query-like commands */ 277e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 278e4a426e7SMarkus Armbruster si = tail->value; 279e4a426e7SMarkus Armbruster if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { 280e4a426e7SMarkus Armbruster continue; 281e4a426e7SMarkus Armbruster } 282e4a426e7SMarkus Armbruster 283e4a426e7SMarkus Armbruster if (query_is_blacklisted(si->name)) { 284e4a426e7SMarkus Armbruster continue; 285e4a426e7SMarkus Armbruster } 286e4a426e7SMarkus Armbruster 287e4a426e7SMarkus Armbruster arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); 288e4a426e7SMarkus Armbruster if (object_type_has_mandatory_members(arg_type)) { 289e4a426e7SMarkus Armbruster continue; 290e4a426e7SMarkus Armbruster } 291e4a426e7SMarkus Armbruster 292e4a426e7SMarkus Armbruster ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); 293e4a426e7SMarkus Armbruster if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT 294e4a426e7SMarkus Armbruster && !ret_type->u.object.members) { 295e4a426e7SMarkus Armbruster continue; 296e4a426e7SMarkus Armbruster } 297e4a426e7SMarkus Armbruster 298e4a426e7SMarkus Armbruster test_name = g_strdup_printf("qmp/%s", si->name); 299e4a426e7SMarkus Armbruster qtest_add_data_func(test_name, si->name, test_query); 300*e313d5ceSMarc-André Lureau g_free(test_name); 301e4a426e7SMarkus Armbruster } 302e4a426e7SMarkus Armbruster } 303e4a426e7SMarkus Armbruster 304f66e7ac8SMarkus Armbruster int main(int argc, char *argv[]) 305f66e7ac8SMarkus Armbruster { 306e4a426e7SMarkus Armbruster QmpSchema schema; 307e4a426e7SMarkus Armbruster int ret; 308e4a426e7SMarkus Armbruster 309f66e7ac8SMarkus Armbruster g_test_init(&argc, &argv, NULL); 310f66e7ac8SMarkus Armbruster 311f66e7ac8SMarkus Armbruster qtest_add_func("qmp/protocol", test_qmp_protocol); 312e4a426e7SMarkus Armbruster qmp_schema_init(&schema); 313e4a426e7SMarkus Armbruster add_query_tests(&schema); 314f66e7ac8SMarkus Armbruster 315e4a426e7SMarkus Armbruster ret = g_test_run(); 316e4a426e7SMarkus Armbruster 317e4a426e7SMarkus Armbruster qmp_schema_cleanup(&schema); 318e4a426e7SMarkus Armbruster return ret; 319f66e7ac8SMarkus Armbruster } 320