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" 18*e4a426e7SMarkus 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 133*e4a426e7SMarkus Armbruster static int query_error_class(const char *cmd) 134*e4a426e7SMarkus Armbruster { 135*e4a426e7SMarkus Armbruster static struct { 136*e4a426e7SMarkus Armbruster const char *cmd; 137*e4a426e7SMarkus Armbruster int err_class; 138*e4a426e7SMarkus Armbruster } fails[] = { 139*e4a426e7SMarkus Armbruster /* Success depends on build configuration: */ 140*e4a426e7SMarkus Armbruster #ifndef CONFIG_SPICE 141*e4a426e7SMarkus Armbruster { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, 142*e4a426e7SMarkus Armbruster #endif 143*e4a426e7SMarkus Armbruster #ifndef CONFIG_VNC 144*e4a426e7SMarkus Armbruster { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, 145*e4a426e7SMarkus Armbruster { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, 146*e4a426e7SMarkus Armbruster #endif 147*e4a426e7SMarkus Armbruster #ifndef CONFIG_REPLICATION 148*e4a426e7SMarkus Armbruster { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, 149*e4a426e7SMarkus Armbruster #endif 150*e4a426e7SMarkus Armbruster /* Likewise, and require special QEMU command-line arguments: */ 151*e4a426e7SMarkus Armbruster { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, 152*e4a426e7SMarkus Armbruster { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, 153*e4a426e7SMarkus Armbruster { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, 154*e4a426e7SMarkus Armbruster { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, 155*e4a426e7SMarkus Armbruster { NULL, -1 } 156*e4a426e7SMarkus Armbruster }; 157*e4a426e7SMarkus Armbruster int i; 158*e4a426e7SMarkus Armbruster 159*e4a426e7SMarkus Armbruster for (i = 0; fails[i].cmd; i++) { 160*e4a426e7SMarkus Armbruster if (!strcmp(cmd, fails[i].cmd)) { 161*e4a426e7SMarkus Armbruster return fails[i].err_class; 162*e4a426e7SMarkus Armbruster } 163*e4a426e7SMarkus Armbruster } 164*e4a426e7SMarkus Armbruster return -1; 165*e4a426e7SMarkus Armbruster } 166*e4a426e7SMarkus Armbruster 167*e4a426e7SMarkus Armbruster static void test_query(const void *data) 168*e4a426e7SMarkus Armbruster { 169*e4a426e7SMarkus Armbruster const char *cmd = data; 170*e4a426e7SMarkus Armbruster int expected_error_class = query_error_class(cmd); 171*e4a426e7SMarkus Armbruster QDict *resp, *error; 172*e4a426e7SMarkus Armbruster const char *error_class; 173*e4a426e7SMarkus Armbruster 174*e4a426e7SMarkus Armbruster qtest_start(common_args); 175*e4a426e7SMarkus Armbruster 176*e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': %s }", cmd); 177*e4a426e7SMarkus Armbruster error = qdict_get_qdict(resp, "error"); 178*e4a426e7SMarkus Armbruster error_class = error ? qdict_get_str(error, "class") : NULL; 179*e4a426e7SMarkus Armbruster 180*e4a426e7SMarkus Armbruster if (expected_error_class < 0) { 181*e4a426e7SMarkus Armbruster g_assert(qdict_haskey(resp, "return")); 182*e4a426e7SMarkus Armbruster } else { 183*e4a426e7SMarkus Armbruster g_assert(error); 184*e4a426e7SMarkus Armbruster g_assert_cmpint(qapi_enum_parse(QapiErrorClass_lookup, error_class, 185*e4a426e7SMarkus Armbruster QAPI_ERROR_CLASS__MAX, -1, 186*e4a426e7SMarkus Armbruster &error_abort), 187*e4a426e7SMarkus Armbruster ==, expected_error_class); 188*e4a426e7SMarkus Armbruster } 189*e4a426e7SMarkus Armbruster QDECREF(resp); 190*e4a426e7SMarkus Armbruster 191*e4a426e7SMarkus Armbruster qtest_end(); 192*e4a426e7SMarkus Armbruster } 193*e4a426e7SMarkus Armbruster 194*e4a426e7SMarkus Armbruster static bool query_is_blacklisted(const char *cmd) 195*e4a426e7SMarkus Armbruster { 196*e4a426e7SMarkus Armbruster const char *blacklist[] = { 197*e4a426e7SMarkus Armbruster /* Not actually queries: */ 198*e4a426e7SMarkus Armbruster "add-fd", 199*e4a426e7SMarkus Armbruster /* Success depends on target arch: */ 200*e4a426e7SMarkus Armbruster "query-cpu-definitions", /* arm, i386, ppc, s390x */ 201*e4a426e7SMarkus Armbruster "query-gic-capabilities", /* arm */ 202*e4a426e7SMarkus Armbruster /* Success depends on target-specific build configuration: */ 203*e4a426e7SMarkus Armbruster "query-pci", /* CONFIG_PCI */ 204*e4a426e7SMarkus Armbruster NULL 205*e4a426e7SMarkus Armbruster }; 206*e4a426e7SMarkus Armbruster int i; 207*e4a426e7SMarkus Armbruster 208*e4a426e7SMarkus Armbruster for (i = 0; blacklist[i]; i++) { 209*e4a426e7SMarkus Armbruster if (!strcmp(cmd, blacklist[i])) { 210*e4a426e7SMarkus Armbruster return true; 211*e4a426e7SMarkus Armbruster } 212*e4a426e7SMarkus Armbruster } 213*e4a426e7SMarkus Armbruster return false; 214*e4a426e7SMarkus Armbruster } 215*e4a426e7SMarkus Armbruster 216*e4a426e7SMarkus Armbruster typedef struct { 217*e4a426e7SMarkus Armbruster SchemaInfoList *list; 218*e4a426e7SMarkus Armbruster GHashTable *hash; 219*e4a426e7SMarkus Armbruster } QmpSchema; 220*e4a426e7SMarkus Armbruster 221*e4a426e7SMarkus Armbruster static void qmp_schema_init(QmpSchema *schema) 222*e4a426e7SMarkus Armbruster { 223*e4a426e7SMarkus Armbruster QDict *resp; 224*e4a426e7SMarkus Armbruster Visitor *qiv; 225*e4a426e7SMarkus Armbruster SchemaInfoList *tail; 226*e4a426e7SMarkus Armbruster 227*e4a426e7SMarkus Armbruster qtest_start(common_args); 228*e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': 'query-qmp-schema' }"); 229*e4a426e7SMarkus Armbruster 230*e4a426e7SMarkus Armbruster qiv = qobject_input_visitor_new(qdict_get(resp, "return")); 231*e4a426e7SMarkus Armbruster visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); 232*e4a426e7SMarkus Armbruster visit_free(qiv); 233*e4a426e7SMarkus Armbruster 234*e4a426e7SMarkus Armbruster QDECREF(resp); 235*e4a426e7SMarkus Armbruster qtest_end(); 236*e4a426e7SMarkus Armbruster 237*e4a426e7SMarkus Armbruster schema->hash = g_hash_table_new(g_str_hash, g_str_equal); 238*e4a426e7SMarkus Armbruster 239*e4a426e7SMarkus Armbruster /* Build @schema: hash table mapping entity name to SchemaInfo */ 240*e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 241*e4a426e7SMarkus Armbruster g_hash_table_insert(schema->hash, tail->value->name, tail->value); 242*e4a426e7SMarkus Armbruster } 243*e4a426e7SMarkus Armbruster } 244*e4a426e7SMarkus Armbruster 245*e4a426e7SMarkus Armbruster static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) 246*e4a426e7SMarkus Armbruster { 247*e4a426e7SMarkus Armbruster return g_hash_table_lookup(schema->hash, name); 248*e4a426e7SMarkus Armbruster } 249*e4a426e7SMarkus Armbruster 250*e4a426e7SMarkus Armbruster static void qmp_schema_cleanup(QmpSchema *schema) 251*e4a426e7SMarkus Armbruster { 252*e4a426e7SMarkus Armbruster qapi_free_SchemaInfoList(schema->list); 253*e4a426e7SMarkus Armbruster g_hash_table_destroy(schema->hash); 254*e4a426e7SMarkus Armbruster } 255*e4a426e7SMarkus Armbruster 256*e4a426e7SMarkus Armbruster static bool object_type_has_mandatory_members(SchemaInfo *type) 257*e4a426e7SMarkus Armbruster { 258*e4a426e7SMarkus Armbruster SchemaInfoObjectMemberList *tail; 259*e4a426e7SMarkus Armbruster 260*e4a426e7SMarkus Armbruster g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); 261*e4a426e7SMarkus Armbruster 262*e4a426e7SMarkus Armbruster for (tail = type->u.object.members; tail; tail = tail->next) { 263*e4a426e7SMarkus Armbruster if (!tail->value->has_q_default) { 264*e4a426e7SMarkus Armbruster return true; 265*e4a426e7SMarkus Armbruster } 266*e4a426e7SMarkus Armbruster } 267*e4a426e7SMarkus Armbruster 268*e4a426e7SMarkus Armbruster return false; 269*e4a426e7SMarkus Armbruster } 270*e4a426e7SMarkus Armbruster 271*e4a426e7SMarkus Armbruster static void add_query_tests(QmpSchema *schema) 272*e4a426e7SMarkus Armbruster { 273*e4a426e7SMarkus Armbruster SchemaInfoList *tail; 274*e4a426e7SMarkus Armbruster SchemaInfo *si, *arg_type, *ret_type; 275*e4a426e7SMarkus Armbruster const char *test_name; 276*e4a426e7SMarkus Armbruster 277*e4a426e7SMarkus Armbruster /* Test the query-like commands */ 278*e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 279*e4a426e7SMarkus Armbruster si = tail->value; 280*e4a426e7SMarkus Armbruster if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { 281*e4a426e7SMarkus Armbruster continue; 282*e4a426e7SMarkus Armbruster } 283*e4a426e7SMarkus Armbruster 284*e4a426e7SMarkus Armbruster if (query_is_blacklisted(si->name)) { 285*e4a426e7SMarkus Armbruster continue; 286*e4a426e7SMarkus Armbruster } 287*e4a426e7SMarkus Armbruster 288*e4a426e7SMarkus Armbruster arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); 289*e4a426e7SMarkus Armbruster if (object_type_has_mandatory_members(arg_type)) { 290*e4a426e7SMarkus Armbruster continue; 291*e4a426e7SMarkus Armbruster } 292*e4a426e7SMarkus Armbruster 293*e4a426e7SMarkus Armbruster ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); 294*e4a426e7SMarkus Armbruster if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT 295*e4a426e7SMarkus Armbruster && !ret_type->u.object.members) { 296*e4a426e7SMarkus Armbruster continue; 297*e4a426e7SMarkus Armbruster } 298*e4a426e7SMarkus Armbruster 299*e4a426e7SMarkus Armbruster test_name = g_strdup_printf("qmp/%s", si->name); 300*e4a426e7SMarkus Armbruster qtest_add_data_func(test_name, si->name, test_query); 301*e4a426e7SMarkus Armbruster } 302*e4a426e7SMarkus Armbruster } 303*e4a426e7SMarkus Armbruster 304f66e7ac8SMarkus Armbruster int main(int argc, char *argv[]) 305f66e7ac8SMarkus Armbruster { 306*e4a426e7SMarkus Armbruster QmpSchema schema; 307*e4a426e7SMarkus Armbruster int ret; 308*e4a426e7SMarkus Armbruster 309f66e7ac8SMarkus Armbruster g_test_init(&argc, &argv, NULL); 310f66e7ac8SMarkus Armbruster 311f66e7ac8SMarkus Armbruster qtest_add_func("qmp/protocol", test_qmp_protocol); 312*e4a426e7SMarkus Armbruster qmp_schema_init(&schema); 313*e4a426e7SMarkus Armbruster add_query_tests(&schema); 314f66e7ac8SMarkus Armbruster 315*e4a426e7SMarkus Armbruster ret = g_test_run(); 316*e4a426e7SMarkus Armbruster 317*e4a426e7SMarkus Armbruster qmp_schema_cleanup(&schema); 318*e4a426e7SMarkus Armbruster return ret; 319f66e7ac8SMarkus Armbruster } 320