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" 2302130314SPeter Xu #include "qapi/qmp/qstring.h" 24f66e7ac8SMarkus Armbruster 25f66e7ac8SMarkus Armbruster const char common_args[] = "-nodefaults -machine none"; 26f66e7ac8SMarkus Armbruster 27f66e7ac8SMarkus Armbruster static const char *get_error_class(QDict *resp) 28f66e7ac8SMarkus Armbruster { 29f66e7ac8SMarkus Armbruster QDict *error = qdict_get_qdict(resp, "error"); 30f66e7ac8SMarkus Armbruster const char *desc = qdict_get_try_str(error, "desc"); 31f66e7ac8SMarkus Armbruster 32f66e7ac8SMarkus Armbruster g_assert(desc); 33f66e7ac8SMarkus Armbruster return error ? qdict_get_try_str(error, "class") : NULL; 34f66e7ac8SMarkus Armbruster } 35f66e7ac8SMarkus Armbruster 36f66e7ac8SMarkus Armbruster static void test_version(QObject *version) 37f66e7ac8SMarkus Armbruster { 38f66e7ac8SMarkus Armbruster Visitor *v; 39f66e7ac8SMarkus Armbruster VersionInfo *vinfo; 40f66e7ac8SMarkus Armbruster 41f66e7ac8SMarkus Armbruster g_assert(version); 42048abb7bSMarkus Armbruster v = qobject_input_visitor_new(version); 43f66e7ac8SMarkus Armbruster visit_type_VersionInfo(v, "version", &vinfo, &error_abort); 44f66e7ac8SMarkus Armbruster qapi_free_VersionInfo(vinfo); 45f66e7ac8SMarkus Armbruster visit_free(v); 46f66e7ac8SMarkus Armbruster } 47f66e7ac8SMarkus Armbruster 486a5c88f5SEric Blake static void test_malformed(QTestState *qts) 49f66e7ac8SMarkus Armbruster { 50f66e7ac8SMarkus Armbruster QDict *resp; 51f66e7ac8SMarkus Armbruster 52f66e7ac8SMarkus Armbruster /* Not even a dictionary */ 536a5c88f5SEric Blake resp = qtest_qmp(qts, "null"); 54f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 55cb3e7f08SMarc-André Lureau qobject_unref(resp); 56f66e7ac8SMarkus Armbruster 57f66e7ac8SMarkus Armbruster /* No "execute" key */ 586a5c88f5SEric Blake resp = qtest_qmp(qts, "{}"); 59f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 60cb3e7f08SMarc-André Lureau qobject_unref(resp); 61f66e7ac8SMarkus Armbruster 62f66e7ac8SMarkus Armbruster /* "execute" isn't a string */ 636a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': true }"); 64f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 65cb3e7f08SMarc-André Lureau qobject_unref(resp); 66f66e7ac8SMarkus Armbruster 67f66e7ac8SMarkus Armbruster /* "arguments" isn't a dictionary */ 686a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); 69f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 70cb3e7f08SMarc-André Lureau qobject_unref(resp); 71f66e7ac8SMarkus Armbruster 72f66e7ac8SMarkus Armbruster /* extra key */ 736a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); 74f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 75cb3e7f08SMarc-André Lureau qobject_unref(resp); 76f66e7ac8SMarkus Armbruster } 77f66e7ac8SMarkus Armbruster 78f66e7ac8SMarkus Armbruster static void test_qmp_protocol(void) 79f66e7ac8SMarkus Armbruster { 80f66e7ac8SMarkus Armbruster QDict *resp, *q, *ret; 81f66e7ac8SMarkus Armbruster QList *capabilities; 826a5c88f5SEric Blake QTestState *qts; 83f66e7ac8SMarkus Armbruster 84ddee57e0SEric Blake qts = qtest_init_without_qmp_handshake(false, common_args); 85f66e7ac8SMarkus Armbruster 86f66e7ac8SMarkus Armbruster /* Test greeting */ 876a5c88f5SEric Blake resp = qtest_qmp_receive(qts); 88f66e7ac8SMarkus Armbruster q = qdict_get_qdict(resp, "QMP"); 89f66e7ac8SMarkus Armbruster g_assert(q); 90f66e7ac8SMarkus Armbruster test_version(qdict_get(q, "version")); 91f66e7ac8SMarkus Armbruster capabilities = qdict_get_qlist(q, "capabilities"); 92a4f90923SPeter Xu g_assert(capabilities && qlist_empty(capabilities)); 93cb3e7f08SMarc-André Lureau qobject_unref(resp); 94f66e7ac8SMarkus Armbruster 95f66e7ac8SMarkus Armbruster /* Test valid command before handshake */ 966a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); 97f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); 98cb3e7f08SMarc-André Lureau qobject_unref(resp); 99f66e7ac8SMarkus Armbruster 100f66e7ac8SMarkus Armbruster /* Test malformed commands before handshake */ 1016a5c88f5SEric Blake test_malformed(qts); 102f66e7ac8SMarkus Armbruster 103f66e7ac8SMarkus Armbruster /* Test handshake */ 1046a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); 105f66e7ac8SMarkus Armbruster ret = qdict_get_qdict(resp, "return"); 106f66e7ac8SMarkus Armbruster g_assert(ret && !qdict_size(ret)); 107cb3e7f08SMarc-André Lureau qobject_unref(resp); 108f66e7ac8SMarkus Armbruster 109f66e7ac8SMarkus Armbruster /* Test repeated handshake */ 1106a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); 111f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); 112cb3e7f08SMarc-André Lureau qobject_unref(resp); 113f66e7ac8SMarkus Armbruster 114f66e7ac8SMarkus Armbruster /* Test valid command */ 1156a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); 116f66e7ac8SMarkus Armbruster test_version(qdict_get(resp, "return")); 117cb3e7f08SMarc-André Lureau qobject_unref(resp); 118f66e7ac8SMarkus Armbruster 119f66e7ac8SMarkus Armbruster /* Test malformed commands */ 1206a5c88f5SEric Blake test_malformed(qts); 121f66e7ac8SMarkus Armbruster 122f66e7ac8SMarkus Armbruster /* Test 'id' */ 1236a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }"); 124f66e7ac8SMarkus Armbruster ret = qdict_get_qdict(resp, "return"); 125f66e7ac8SMarkus Armbruster g_assert(ret); 126f66e7ac8SMarkus Armbruster g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); 127cb3e7f08SMarc-André Lureau qobject_unref(resp); 128f66e7ac8SMarkus Armbruster 129f66e7ac8SMarkus Armbruster /* Test command failure with 'id' */ 1306a5c88f5SEric Blake resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); 131f66e7ac8SMarkus Armbruster g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); 132f66e7ac8SMarkus Armbruster g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); 133cb3e7f08SMarc-André Lureau qobject_unref(resp); 134f66e7ac8SMarkus Armbruster 1356a5c88f5SEric Blake qtest_quit(qts); 136f66e7ac8SMarkus Armbruster } 137f66e7ac8SMarkus Armbruster 13897ca0712SMarkus Armbruster /* Out-of-band tests */ 13997ca0712SMarkus Armbruster 14097ca0712SMarkus Armbruster char tmpdir[] = "/tmp/qmp-test-XXXXXX"; 14197ca0712SMarkus Armbruster char *fifo_name; 14297ca0712SMarkus Armbruster 14397ca0712SMarkus Armbruster static void setup_blocking_cmd(void) 14497ca0712SMarkus Armbruster { 14597ca0712SMarkus Armbruster if (!mkdtemp(tmpdir)) { 14697ca0712SMarkus Armbruster g_error("mkdtemp: %s", strerror(errno)); 14797ca0712SMarkus Armbruster } 14897ca0712SMarkus Armbruster fifo_name = g_strdup_printf("%s/fifo", tmpdir); 14997ca0712SMarkus Armbruster if (mkfifo(fifo_name, 0666)) { 15097ca0712SMarkus Armbruster g_error("mkfifo: %s", strerror(errno)); 15197ca0712SMarkus Armbruster } 15297ca0712SMarkus Armbruster } 15397ca0712SMarkus Armbruster 15497ca0712SMarkus Armbruster static void cleanup_blocking_cmd(void) 15597ca0712SMarkus Armbruster { 15697ca0712SMarkus Armbruster unlink(fifo_name); 15797ca0712SMarkus Armbruster rmdir(tmpdir); 15897ca0712SMarkus Armbruster } 15997ca0712SMarkus Armbruster 16097ca0712SMarkus Armbruster static void send_cmd_that_blocks(QTestState *s, const char *id) 16197ca0712SMarkus Armbruster { 16297ca0712SMarkus Armbruster qtest_async_qmp(s, "{ 'execute': 'blockdev-add', 'id': %s," 16397ca0712SMarkus Armbruster " 'arguments': {" 16497ca0712SMarkus Armbruster " 'driver': 'blkdebug', 'node-name': %s," 16597ca0712SMarkus Armbruster " 'config': %s," 16697ca0712SMarkus Armbruster " 'image': { 'driver': 'null-co' } } }", 16797ca0712SMarkus Armbruster id, id, fifo_name); 16897ca0712SMarkus Armbruster } 16997ca0712SMarkus Armbruster 17097ca0712SMarkus Armbruster static void unblock_blocked_cmd(void) 17197ca0712SMarkus Armbruster { 17297ca0712SMarkus Armbruster int fd = open(fifo_name, O_WRONLY); 17397ca0712SMarkus Armbruster g_assert(fd >= 0); 17497ca0712SMarkus Armbruster close(fd); 17597ca0712SMarkus Armbruster } 17697ca0712SMarkus Armbruster 17797ca0712SMarkus Armbruster static void send_oob_cmd_that_fails(QTestState *s, const char *id) 17897ca0712SMarkus Armbruster { 17900ecec15SMarkus Armbruster qtest_async_qmp(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id); 18097ca0712SMarkus Armbruster } 18197ca0712SMarkus Armbruster 18297ca0712SMarkus Armbruster static void recv_cmd_id(QTestState *s, const char *id) 18397ca0712SMarkus Armbruster { 18497ca0712SMarkus Armbruster QDict *resp = qtest_qmp_receive(s); 18597ca0712SMarkus Armbruster 18697ca0712SMarkus Armbruster g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id); 18797ca0712SMarkus Armbruster qobject_unref(resp); 18897ca0712SMarkus Armbruster } 18997ca0712SMarkus Armbruster 190fa198ad9SPeter Xu static void test_qmp_oob(void) 191fa198ad9SPeter Xu { 192fa198ad9SPeter Xu QTestState *qts; 193fa198ad9SPeter Xu QDict *resp, *q; 194fa198ad9SPeter Xu const QListEntry *entry; 195fa198ad9SPeter Xu QList *capabilities; 196fa198ad9SPeter Xu QString *qstr; 197fa198ad9SPeter Xu 198fa198ad9SPeter Xu qts = qtest_init_without_qmp_handshake(true, common_args); 199fa198ad9SPeter Xu 200fa198ad9SPeter Xu /* Check the greeting message. */ 201fa198ad9SPeter Xu resp = qtest_qmp_receive(qts); 202fa198ad9SPeter Xu q = qdict_get_qdict(resp, "QMP"); 203fa198ad9SPeter Xu g_assert(q); 204fa198ad9SPeter Xu capabilities = qdict_get_qlist(q, "capabilities"); 205fa198ad9SPeter Xu g_assert(capabilities && !qlist_empty(capabilities)); 206fa198ad9SPeter Xu entry = qlist_first(capabilities); 207fa198ad9SPeter Xu g_assert(entry); 208fa198ad9SPeter Xu qstr = qobject_to(QString, entry->value); 209fa198ad9SPeter Xu g_assert(qstr); 210fa198ad9SPeter Xu g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); 211cb3e7f08SMarc-André Lureau qobject_unref(resp); 212fa198ad9SPeter Xu 213fa198ad9SPeter Xu /* Try a fake capability, it should fail. */ 214fa198ad9SPeter Xu resp = qtest_qmp(qts, 215fa198ad9SPeter Xu "{ 'execute': 'qmp_capabilities', " 216fa198ad9SPeter Xu " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); 217fa198ad9SPeter Xu g_assert(qdict_haskey(resp, "error")); 218cb3e7f08SMarc-André Lureau qobject_unref(resp); 219fa198ad9SPeter Xu 220fa198ad9SPeter Xu /* Now, enable OOB in current QMP session, it should succeed. */ 221fa198ad9SPeter Xu resp = qtest_qmp(qts, 222fa198ad9SPeter Xu "{ 'execute': 'qmp_capabilities', " 223fa198ad9SPeter Xu " 'arguments': { 'enable': [ 'oob' ] } }"); 224fa198ad9SPeter Xu g_assert(qdict_haskey(resp, "return")); 225cb3e7f08SMarc-André Lureau qobject_unref(resp); 226fa198ad9SPeter Xu 227fa198ad9SPeter Xu /* 228fa198ad9SPeter Xu * Try any command that does not support OOB but with OOB flag. We 229fa198ad9SPeter Xu * should get failure. 230fa198ad9SPeter Xu */ 23100ecec15SMarkus Armbruster resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }"); 232fa198ad9SPeter Xu g_assert(qdict_haskey(resp, "error")); 233cb3e7f08SMarc-André Lureau qobject_unref(resp); 234fa198ad9SPeter Xu 23597ca0712SMarkus Armbruster /* OOB command overtakes slow in-band command */ 23697ca0712SMarkus Armbruster setup_blocking_cmd(); 23797ca0712SMarkus Armbruster send_cmd_that_blocks(qts, "ib-blocks-1"); 2382970b446SMarkus Armbruster qtest_async_qmp(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }"); 23997ca0712SMarkus Armbruster send_oob_cmd_that_fails(qts, "oob-1"); 24097ca0712SMarkus Armbruster recv_cmd_id(qts, "oob-1"); 24197ca0712SMarkus Armbruster unblock_blocked_cmd(); 24297ca0712SMarkus Armbruster recv_cmd_id(qts, "ib-blocks-1"); 2432970b446SMarkus Armbruster recv_cmd_id(qts, "ib-quick-1"); 244e8f4a221SMarkus Armbruster 245*69240fe6SMarkus Armbruster /* Even malformed in-band command fails in-band */ 246e8f4a221SMarkus Armbruster send_cmd_that_blocks(qts, "blocks-2"); 247e8f4a221SMarkus Armbruster qtest_async_qmp(qts, "{ 'id': 'err-2' }"); 248e8f4a221SMarkus Armbruster unblock_blocked_cmd(); 249e8f4a221SMarkus Armbruster recv_cmd_id(qts, "blocks-2"); 250*69240fe6SMarkus Armbruster recv_cmd_id(qts, "err-2"); 25197ca0712SMarkus Armbruster cleanup_blocking_cmd(); 252fa198ad9SPeter Xu 253fa198ad9SPeter Xu qtest_quit(qts); 254fa198ad9SPeter Xu } 255fa198ad9SPeter Xu 25697ca0712SMarkus Armbruster /* Query smoke tests */ 25797ca0712SMarkus Armbruster 258e4a426e7SMarkus Armbruster static int query_error_class(const char *cmd) 259e4a426e7SMarkus Armbruster { 260e4a426e7SMarkus Armbruster static struct { 261e4a426e7SMarkus Armbruster const char *cmd; 262e4a426e7SMarkus Armbruster int err_class; 263e4a426e7SMarkus Armbruster } fails[] = { 264e4a426e7SMarkus Armbruster /* Success depends on build configuration: */ 265e4a426e7SMarkus Armbruster #ifndef CONFIG_SPICE 266e4a426e7SMarkus Armbruster { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, 267e4a426e7SMarkus Armbruster #endif 268e4a426e7SMarkus Armbruster #ifndef CONFIG_VNC 269e4a426e7SMarkus Armbruster { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, 270e4a426e7SMarkus Armbruster { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, 271e4a426e7SMarkus Armbruster #endif 272e4a426e7SMarkus Armbruster #ifndef CONFIG_REPLICATION 273e4a426e7SMarkus Armbruster { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, 274e4a426e7SMarkus Armbruster #endif 275e4a426e7SMarkus Armbruster /* Likewise, and require special QEMU command-line arguments: */ 276e4a426e7SMarkus Armbruster { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, 277e4a426e7SMarkus Armbruster { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, 278e4a426e7SMarkus Armbruster { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, 279e4a426e7SMarkus Armbruster { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, 280e4a426e7SMarkus Armbruster { NULL, -1 } 281e4a426e7SMarkus Armbruster }; 282e4a426e7SMarkus Armbruster int i; 283e4a426e7SMarkus Armbruster 284e4a426e7SMarkus Armbruster for (i = 0; fails[i].cmd; i++) { 285e4a426e7SMarkus Armbruster if (!strcmp(cmd, fails[i].cmd)) { 286e4a426e7SMarkus Armbruster return fails[i].err_class; 287e4a426e7SMarkus Armbruster } 288e4a426e7SMarkus Armbruster } 289e4a426e7SMarkus Armbruster return -1; 290e4a426e7SMarkus Armbruster } 291e4a426e7SMarkus Armbruster 292e4a426e7SMarkus Armbruster static void test_query(const void *data) 293e4a426e7SMarkus Armbruster { 294e4a426e7SMarkus Armbruster const char *cmd = data; 295e4a426e7SMarkus Armbruster int expected_error_class = query_error_class(cmd); 296e4a426e7SMarkus Armbruster QDict *resp, *error; 297e4a426e7SMarkus Armbruster const char *error_class; 298e4a426e7SMarkus Armbruster 299e4a426e7SMarkus Armbruster qtest_start(common_args); 300e4a426e7SMarkus Armbruster 301e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': %s }", cmd); 302e4a426e7SMarkus Armbruster error = qdict_get_qdict(resp, "error"); 303e4a426e7SMarkus Armbruster error_class = error ? qdict_get_str(error, "class") : NULL; 304e4a426e7SMarkus Armbruster 305e4a426e7SMarkus Armbruster if (expected_error_class < 0) { 306e4a426e7SMarkus Armbruster g_assert(qdict_haskey(resp, "return")); 307e4a426e7SMarkus Armbruster } else { 308e4a426e7SMarkus Armbruster g_assert(error); 309f7abe0ecSMarc-André Lureau g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, 31006c60b6cSMarkus Armbruster -1, &error_abort), 311e4a426e7SMarkus Armbruster ==, expected_error_class); 312e4a426e7SMarkus Armbruster } 313cb3e7f08SMarc-André Lureau qobject_unref(resp); 314e4a426e7SMarkus Armbruster 315e4a426e7SMarkus Armbruster qtest_end(); 316e4a426e7SMarkus Armbruster } 317e4a426e7SMarkus Armbruster 318e4a426e7SMarkus Armbruster static bool query_is_blacklisted(const char *cmd) 319e4a426e7SMarkus Armbruster { 320e4a426e7SMarkus Armbruster const char *blacklist[] = { 321e4a426e7SMarkus Armbruster /* Not actually queries: */ 322e4a426e7SMarkus Armbruster "add-fd", 323e4a426e7SMarkus Armbruster /* Success depends on target arch: */ 324e4a426e7SMarkus Armbruster "query-cpu-definitions", /* arm, i386, ppc, s390x */ 325e4a426e7SMarkus Armbruster "query-gic-capabilities", /* arm */ 326e4a426e7SMarkus Armbruster /* Success depends on target-specific build configuration: */ 327e4a426e7SMarkus Armbruster "query-pci", /* CONFIG_PCI */ 3281b6a034fSBrijesh Singh /* Success depends on launching SEV guest */ 3291b6a034fSBrijesh Singh "query-sev-launch-measure", 33008a161fdSBrijesh Singh /* Success depends on Host or Hypervisor SEV support */ 33108a161fdSBrijesh Singh "query-sev", 33231dd67f6SBrijesh Singh "query-sev-capabilities", 333e4a426e7SMarkus Armbruster NULL 334e4a426e7SMarkus Armbruster }; 335e4a426e7SMarkus Armbruster int i; 336e4a426e7SMarkus Armbruster 337e4a426e7SMarkus Armbruster for (i = 0; blacklist[i]; i++) { 338e4a426e7SMarkus Armbruster if (!strcmp(cmd, blacklist[i])) { 339e4a426e7SMarkus Armbruster return true; 340e4a426e7SMarkus Armbruster } 341e4a426e7SMarkus Armbruster } 342e4a426e7SMarkus Armbruster return false; 343e4a426e7SMarkus Armbruster } 344e4a426e7SMarkus Armbruster 345e4a426e7SMarkus Armbruster typedef struct { 346e4a426e7SMarkus Armbruster SchemaInfoList *list; 347e4a426e7SMarkus Armbruster GHashTable *hash; 348e4a426e7SMarkus Armbruster } QmpSchema; 349e4a426e7SMarkus Armbruster 350e4a426e7SMarkus Armbruster static void qmp_schema_init(QmpSchema *schema) 351e4a426e7SMarkus Armbruster { 352e4a426e7SMarkus Armbruster QDict *resp; 353e4a426e7SMarkus Armbruster Visitor *qiv; 354e4a426e7SMarkus Armbruster SchemaInfoList *tail; 355e4a426e7SMarkus Armbruster 356e4a426e7SMarkus Armbruster qtest_start(common_args); 357e4a426e7SMarkus Armbruster resp = qmp("{ 'execute': 'query-qmp-schema' }"); 358e4a426e7SMarkus Armbruster 359e4a426e7SMarkus Armbruster qiv = qobject_input_visitor_new(qdict_get(resp, "return")); 360e4a426e7SMarkus Armbruster visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); 361e4a426e7SMarkus Armbruster visit_free(qiv); 362e4a426e7SMarkus Armbruster 363cb3e7f08SMarc-André Lureau qobject_unref(resp); 364e4a426e7SMarkus Armbruster qtest_end(); 365e4a426e7SMarkus Armbruster 366e4a426e7SMarkus Armbruster schema->hash = g_hash_table_new(g_str_hash, g_str_equal); 367e4a426e7SMarkus Armbruster 368e4a426e7SMarkus Armbruster /* Build @schema: hash table mapping entity name to SchemaInfo */ 369e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 370e4a426e7SMarkus Armbruster g_hash_table_insert(schema->hash, tail->value->name, tail->value); 371e4a426e7SMarkus Armbruster } 372e4a426e7SMarkus Armbruster } 373e4a426e7SMarkus Armbruster 374e4a426e7SMarkus Armbruster static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) 375e4a426e7SMarkus Armbruster { 376e4a426e7SMarkus Armbruster return g_hash_table_lookup(schema->hash, name); 377e4a426e7SMarkus Armbruster } 378e4a426e7SMarkus Armbruster 379e4a426e7SMarkus Armbruster static void qmp_schema_cleanup(QmpSchema *schema) 380e4a426e7SMarkus Armbruster { 381e4a426e7SMarkus Armbruster qapi_free_SchemaInfoList(schema->list); 382e4a426e7SMarkus Armbruster g_hash_table_destroy(schema->hash); 383e4a426e7SMarkus Armbruster } 384e4a426e7SMarkus Armbruster 385e4a426e7SMarkus Armbruster static bool object_type_has_mandatory_members(SchemaInfo *type) 386e4a426e7SMarkus Armbruster { 387e4a426e7SMarkus Armbruster SchemaInfoObjectMemberList *tail; 388e4a426e7SMarkus Armbruster 389e4a426e7SMarkus Armbruster g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); 390e4a426e7SMarkus Armbruster 391e4a426e7SMarkus Armbruster for (tail = type->u.object.members; tail; tail = tail->next) { 392e4a426e7SMarkus Armbruster if (!tail->value->has_q_default) { 393e4a426e7SMarkus Armbruster return true; 394e4a426e7SMarkus Armbruster } 395e4a426e7SMarkus Armbruster } 396e4a426e7SMarkus Armbruster 397e4a426e7SMarkus Armbruster return false; 398e4a426e7SMarkus Armbruster } 399e4a426e7SMarkus Armbruster 400e4a426e7SMarkus Armbruster static void add_query_tests(QmpSchema *schema) 401e4a426e7SMarkus Armbruster { 402e4a426e7SMarkus Armbruster SchemaInfoList *tail; 403e4a426e7SMarkus Armbruster SchemaInfo *si, *arg_type, *ret_type; 404e313d5ceSMarc-André Lureau char *test_name; 405e4a426e7SMarkus Armbruster 406e4a426e7SMarkus Armbruster /* Test the query-like commands */ 407e4a426e7SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 408e4a426e7SMarkus Armbruster si = tail->value; 409e4a426e7SMarkus Armbruster if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { 410e4a426e7SMarkus Armbruster continue; 411e4a426e7SMarkus Armbruster } 412e4a426e7SMarkus Armbruster 413e4a426e7SMarkus Armbruster if (query_is_blacklisted(si->name)) { 414e4a426e7SMarkus Armbruster continue; 415e4a426e7SMarkus Armbruster } 416e4a426e7SMarkus Armbruster 417e4a426e7SMarkus Armbruster arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); 418e4a426e7SMarkus Armbruster if (object_type_has_mandatory_members(arg_type)) { 419e4a426e7SMarkus Armbruster continue; 420e4a426e7SMarkus Armbruster } 421e4a426e7SMarkus Armbruster 422e4a426e7SMarkus Armbruster ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); 423e4a426e7SMarkus Armbruster if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT 424e4a426e7SMarkus Armbruster && !ret_type->u.object.members) { 425e4a426e7SMarkus Armbruster continue; 426e4a426e7SMarkus Armbruster } 427e4a426e7SMarkus Armbruster 428e4a426e7SMarkus Armbruster test_name = g_strdup_printf("qmp/%s", si->name); 429e4a426e7SMarkus Armbruster qtest_add_data_func(test_name, si->name, test_query); 430e313d5ceSMarc-André Lureau g_free(test_name); 431e4a426e7SMarkus Armbruster } 432e4a426e7SMarkus Armbruster } 433e4a426e7SMarkus Armbruster 43497ca0712SMarkus Armbruster /* Preconfig tests */ 43597ca0712SMarkus Armbruster 436fb1e58f7SIgor Mammedov static void test_qmp_preconfig(void) 437fb1e58f7SIgor Mammedov { 438fb1e58f7SIgor Mammedov QDict *rsp, *ret; 439fb1e58f7SIgor Mammedov QTestState *qs = qtest_startf("%s --preconfig", common_args); 440fb1e58f7SIgor Mammedov 441fb1e58f7SIgor Mammedov /* preconfig state */ 442fb1e58f7SIgor Mammedov /* enabled commands, no error expected */ 443fb1e58f7SIgor Mammedov g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }"))); 444fb1e58f7SIgor Mammedov 445fb1e58f7SIgor Mammedov /* forbidden commands, expected error */ 446fb1e58f7SIgor Mammedov g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); 447fb1e58f7SIgor Mammedov 448fb1e58f7SIgor Mammedov /* check that query-status returns preconfig state */ 449fb1e58f7SIgor Mammedov rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); 450fb1e58f7SIgor Mammedov ret = qdict_get_qdict(rsp, "return"); 451fb1e58f7SIgor Mammedov g_assert(ret); 452fb1e58f7SIgor Mammedov g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig"); 453fb1e58f7SIgor Mammedov qobject_unref(rsp); 454fb1e58f7SIgor Mammedov 455fb1e58f7SIgor Mammedov /* exit preconfig state */ 456fb1e58f7SIgor Mammedov g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); 457fb1e58f7SIgor Mammedov qtest_qmp_eventwait(qs, "RESUME"); 458fb1e58f7SIgor Mammedov 459fb1e58f7SIgor Mammedov /* check that query-status returns running state */ 460fb1e58f7SIgor Mammedov rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); 461fb1e58f7SIgor Mammedov ret = qdict_get_qdict(rsp, "return"); 462fb1e58f7SIgor Mammedov g_assert(ret); 463fb1e58f7SIgor Mammedov g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running"); 464fb1e58f7SIgor Mammedov qobject_unref(rsp); 465fb1e58f7SIgor Mammedov 466fb1e58f7SIgor Mammedov /* check that exit-preconfig returns error after exiting preconfig */ 467fb1e58f7SIgor Mammedov g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); 468fb1e58f7SIgor Mammedov 469fb1e58f7SIgor Mammedov /* enabled commands, no error expected */ 470fb1e58f7SIgor Mammedov g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); 471fb1e58f7SIgor Mammedov 472fb1e58f7SIgor Mammedov qtest_quit(qs); 473fb1e58f7SIgor Mammedov } 474fb1e58f7SIgor Mammedov 475f66e7ac8SMarkus Armbruster int main(int argc, char *argv[]) 476f66e7ac8SMarkus Armbruster { 477e4a426e7SMarkus Armbruster QmpSchema schema; 478e4a426e7SMarkus Armbruster int ret; 479e4a426e7SMarkus Armbruster 480f66e7ac8SMarkus Armbruster g_test_init(&argc, &argv, NULL); 481f66e7ac8SMarkus Armbruster 482f66e7ac8SMarkus Armbruster qtest_add_func("qmp/protocol", test_qmp_protocol); 483fa198ad9SPeter Xu qtest_add_func("qmp/oob", test_qmp_oob); 484e4a426e7SMarkus Armbruster qmp_schema_init(&schema); 485e4a426e7SMarkus Armbruster add_query_tests(&schema); 486fb1e58f7SIgor Mammedov qtest_add_func("qmp/preconfig", test_qmp_preconfig); 487f66e7ac8SMarkus Armbruster 488e4a426e7SMarkus Armbruster ret = g_test_run(); 489e4a426e7SMarkus Armbruster 490e4a426e7SMarkus Armbruster qmp_schema_cleanup(&schema); 491e4a426e7SMarkus Armbruster return ret; 492f66e7ac8SMarkus Armbruster } 493