1d93bb9d5SMarkus Armbruster /* 2d93bb9d5SMarkus Armbruster * QMP command test cases 3d93bb9d5SMarkus Armbruster * 4d93bb9d5SMarkus Armbruster * Copyright (c) 2017 Red Hat Inc. 5d93bb9d5SMarkus Armbruster * 6d93bb9d5SMarkus Armbruster * Authors: 7d93bb9d5SMarkus Armbruster * Markus Armbruster <armbru@redhat.com> 8d93bb9d5SMarkus Armbruster * 9d93bb9d5SMarkus Armbruster * This work is licensed under the terms of the GNU GPL, version 2 or later. 10d93bb9d5SMarkus Armbruster * See the COPYING file in the top-level directory. 11d93bb9d5SMarkus Armbruster */ 12d93bb9d5SMarkus Armbruster 13d93bb9d5SMarkus Armbruster #include "qemu/osdep.h" 14d93bb9d5SMarkus Armbruster #include "libqtest.h" 15d93bb9d5SMarkus Armbruster #include "qapi/error.h" 16d93bb9d5SMarkus Armbruster #include "qapi/qapi-visit-introspect.h" 17d93bb9d5SMarkus Armbruster #include "qapi/qmp/qdict.h" 18d93bb9d5SMarkus Armbruster #include "qapi/qobject-input-visitor.h" 19d93bb9d5SMarkus Armbruster 20d93bb9d5SMarkus Armbruster const char common_args[] = "-nodefaults -machine none"; 21d93bb9d5SMarkus Armbruster 22d93bb9d5SMarkus Armbruster /* Query smoke tests */ 23d93bb9d5SMarkus Armbruster 24d93bb9d5SMarkus Armbruster static int query_error_class(const char *cmd) 25d93bb9d5SMarkus Armbruster { 26d93bb9d5SMarkus Armbruster static struct { 27d93bb9d5SMarkus Armbruster const char *cmd; 28d93bb9d5SMarkus Armbruster int err_class; 29d93bb9d5SMarkus Armbruster } fails[] = { 30d93bb9d5SMarkus Armbruster /* Success depends on build configuration: */ 31d93bb9d5SMarkus Armbruster #ifndef CONFIG_SPICE 32d93bb9d5SMarkus Armbruster { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, 33d93bb9d5SMarkus Armbruster #endif 34d93bb9d5SMarkus Armbruster #ifndef CONFIG_VNC 35d93bb9d5SMarkus Armbruster { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, 36d93bb9d5SMarkus Armbruster { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, 37d93bb9d5SMarkus Armbruster #endif 38d93bb9d5SMarkus Armbruster #ifndef CONFIG_REPLICATION 39d93bb9d5SMarkus Armbruster { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, 40d93bb9d5SMarkus Armbruster #endif 41d93bb9d5SMarkus Armbruster /* Likewise, and require special QEMU command-line arguments: */ 42d93bb9d5SMarkus Armbruster { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, 43d93bb9d5SMarkus Armbruster { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, 44d93bb9d5SMarkus Armbruster { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, 45d93bb9d5SMarkus Armbruster { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, 46d93bb9d5SMarkus Armbruster { NULL, -1 } 47d93bb9d5SMarkus Armbruster }; 48d93bb9d5SMarkus Armbruster int i; 49d93bb9d5SMarkus Armbruster 50d93bb9d5SMarkus Armbruster for (i = 0; fails[i].cmd; i++) { 51d93bb9d5SMarkus Armbruster if (!strcmp(cmd, fails[i].cmd)) { 52d93bb9d5SMarkus Armbruster return fails[i].err_class; 53d93bb9d5SMarkus Armbruster } 54d93bb9d5SMarkus Armbruster } 55d93bb9d5SMarkus Armbruster return -1; 56d93bb9d5SMarkus Armbruster } 57d93bb9d5SMarkus Armbruster 58d93bb9d5SMarkus Armbruster static void test_query(const void *data) 59d93bb9d5SMarkus Armbruster { 60d93bb9d5SMarkus Armbruster const char *cmd = data; 61d93bb9d5SMarkus Armbruster int expected_error_class = query_error_class(cmd); 62d93bb9d5SMarkus Armbruster QDict *resp, *error; 63d93bb9d5SMarkus Armbruster const char *error_class; 64da9cd2d0SThomas Huth QTestState *qts; 65d93bb9d5SMarkus Armbruster 66da9cd2d0SThomas Huth qts = qtest_init(common_args); 67d93bb9d5SMarkus Armbruster 68da9cd2d0SThomas Huth resp = qtest_qmp(qts, "{ 'execute': %s }", cmd); 69d93bb9d5SMarkus Armbruster error = qdict_get_qdict(resp, "error"); 70d93bb9d5SMarkus Armbruster error_class = error ? qdict_get_str(error, "class") : NULL; 71d93bb9d5SMarkus Armbruster 72d93bb9d5SMarkus Armbruster if (expected_error_class < 0) { 73d93bb9d5SMarkus Armbruster g_assert(qdict_haskey(resp, "return")); 74d93bb9d5SMarkus Armbruster } else { 75d93bb9d5SMarkus Armbruster g_assert(error); 76d93bb9d5SMarkus Armbruster g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, 77d93bb9d5SMarkus Armbruster -1, &error_abort), 78d93bb9d5SMarkus Armbruster ==, expected_error_class); 79d93bb9d5SMarkus Armbruster } 80d93bb9d5SMarkus Armbruster qobject_unref(resp); 81d93bb9d5SMarkus Armbruster 82da9cd2d0SThomas Huth qtest_quit(qts); 83d93bb9d5SMarkus Armbruster } 84d93bb9d5SMarkus Armbruster 85d93bb9d5SMarkus Armbruster static bool query_is_blacklisted(const char *cmd) 86d93bb9d5SMarkus Armbruster { 87d93bb9d5SMarkus Armbruster const char *blacklist[] = { 88d93bb9d5SMarkus Armbruster /* Not actually queries: */ 89d93bb9d5SMarkus Armbruster "add-fd", 90d93bb9d5SMarkus Armbruster /* Success depends on target arch: */ 91d93bb9d5SMarkus Armbruster "query-cpu-definitions", /* arm, i386, ppc, s390x */ 92d93bb9d5SMarkus Armbruster "query-gic-capabilities", /* arm */ 93d93bb9d5SMarkus Armbruster /* Success depends on target-specific build configuration: */ 94d93bb9d5SMarkus Armbruster "query-pci", /* CONFIG_PCI */ 95d93bb9d5SMarkus Armbruster /* Success depends on launching SEV guest */ 96d93bb9d5SMarkus Armbruster "query-sev-launch-measure", 97d93bb9d5SMarkus Armbruster /* Success depends on Host or Hypervisor SEV support */ 98d93bb9d5SMarkus Armbruster "query-sev", 99d93bb9d5SMarkus Armbruster "query-sev-capabilities", 100d93bb9d5SMarkus Armbruster NULL 101d93bb9d5SMarkus Armbruster }; 102d93bb9d5SMarkus Armbruster int i; 103d93bb9d5SMarkus Armbruster 104d93bb9d5SMarkus Armbruster for (i = 0; blacklist[i]; i++) { 105d93bb9d5SMarkus Armbruster if (!strcmp(cmd, blacklist[i])) { 106d93bb9d5SMarkus Armbruster return true; 107d93bb9d5SMarkus Armbruster } 108d93bb9d5SMarkus Armbruster } 109d93bb9d5SMarkus Armbruster return false; 110d93bb9d5SMarkus Armbruster } 111d93bb9d5SMarkus Armbruster 112d93bb9d5SMarkus Armbruster typedef struct { 113d93bb9d5SMarkus Armbruster SchemaInfoList *list; 114d93bb9d5SMarkus Armbruster GHashTable *hash; 115d93bb9d5SMarkus Armbruster } QmpSchema; 116d93bb9d5SMarkus Armbruster 117d93bb9d5SMarkus Armbruster static void qmp_schema_init(QmpSchema *schema) 118d93bb9d5SMarkus Armbruster { 119d93bb9d5SMarkus Armbruster QDict *resp; 120d93bb9d5SMarkus Armbruster Visitor *qiv; 121d93bb9d5SMarkus Armbruster SchemaInfoList *tail; 122da9cd2d0SThomas Huth QTestState *qts; 123d93bb9d5SMarkus Armbruster 124da9cd2d0SThomas Huth qts = qtest_init(common_args); 125da9cd2d0SThomas Huth 126da9cd2d0SThomas Huth resp = qtest_qmp(qts, "{ 'execute': 'query-qmp-schema' }"); 127d93bb9d5SMarkus Armbruster 128d93bb9d5SMarkus Armbruster qiv = qobject_input_visitor_new(qdict_get(resp, "return")); 129d93bb9d5SMarkus Armbruster visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); 130d93bb9d5SMarkus Armbruster visit_free(qiv); 131d93bb9d5SMarkus Armbruster 132d93bb9d5SMarkus Armbruster qobject_unref(resp); 133da9cd2d0SThomas Huth qtest_quit(qts); 134d93bb9d5SMarkus Armbruster 135d93bb9d5SMarkus Armbruster schema->hash = g_hash_table_new(g_str_hash, g_str_equal); 136d93bb9d5SMarkus Armbruster 137d93bb9d5SMarkus Armbruster /* Build @schema: hash table mapping entity name to SchemaInfo */ 138d93bb9d5SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 139d93bb9d5SMarkus Armbruster g_hash_table_insert(schema->hash, tail->value->name, tail->value); 140d93bb9d5SMarkus Armbruster } 141d93bb9d5SMarkus Armbruster } 142d93bb9d5SMarkus Armbruster 143d93bb9d5SMarkus Armbruster static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) 144d93bb9d5SMarkus Armbruster { 145d93bb9d5SMarkus Armbruster return g_hash_table_lookup(schema->hash, name); 146d93bb9d5SMarkus Armbruster } 147d93bb9d5SMarkus Armbruster 148d93bb9d5SMarkus Armbruster static void qmp_schema_cleanup(QmpSchema *schema) 149d93bb9d5SMarkus Armbruster { 150d93bb9d5SMarkus Armbruster qapi_free_SchemaInfoList(schema->list); 151d93bb9d5SMarkus Armbruster g_hash_table_destroy(schema->hash); 152d93bb9d5SMarkus Armbruster } 153d93bb9d5SMarkus Armbruster 154d93bb9d5SMarkus Armbruster static bool object_type_has_mandatory_members(SchemaInfo *type) 155d93bb9d5SMarkus Armbruster { 156d93bb9d5SMarkus Armbruster SchemaInfoObjectMemberList *tail; 157d93bb9d5SMarkus Armbruster 158d93bb9d5SMarkus Armbruster g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); 159d93bb9d5SMarkus Armbruster 160d93bb9d5SMarkus Armbruster for (tail = type->u.object.members; tail; tail = tail->next) { 161d93bb9d5SMarkus Armbruster if (!tail->value->has_q_default) { 162d93bb9d5SMarkus Armbruster return true; 163d93bb9d5SMarkus Armbruster } 164d93bb9d5SMarkus Armbruster } 165d93bb9d5SMarkus Armbruster 166d93bb9d5SMarkus Armbruster return false; 167d93bb9d5SMarkus Armbruster } 168d93bb9d5SMarkus Armbruster 169d93bb9d5SMarkus Armbruster static void add_query_tests(QmpSchema *schema) 170d93bb9d5SMarkus Armbruster { 171d93bb9d5SMarkus Armbruster SchemaInfoList *tail; 172d93bb9d5SMarkus Armbruster SchemaInfo *si, *arg_type, *ret_type; 173d93bb9d5SMarkus Armbruster char *test_name; 174d93bb9d5SMarkus Armbruster 175d93bb9d5SMarkus Armbruster /* Test the query-like commands */ 176d93bb9d5SMarkus Armbruster for (tail = schema->list; tail; tail = tail->next) { 177d93bb9d5SMarkus Armbruster si = tail->value; 178d93bb9d5SMarkus Armbruster if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { 179d93bb9d5SMarkus Armbruster continue; 180d93bb9d5SMarkus Armbruster } 181d93bb9d5SMarkus Armbruster 182d93bb9d5SMarkus Armbruster if (query_is_blacklisted(si->name)) { 183d93bb9d5SMarkus Armbruster continue; 184d93bb9d5SMarkus Armbruster } 185d93bb9d5SMarkus Armbruster 186d93bb9d5SMarkus Armbruster arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); 187d93bb9d5SMarkus Armbruster if (object_type_has_mandatory_members(arg_type)) { 188d93bb9d5SMarkus Armbruster continue; 189d93bb9d5SMarkus Armbruster } 190d93bb9d5SMarkus Armbruster 191d93bb9d5SMarkus Armbruster ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); 192d93bb9d5SMarkus Armbruster if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT 193d93bb9d5SMarkus Armbruster && !ret_type->u.object.members) { 194d93bb9d5SMarkus Armbruster continue; 195d93bb9d5SMarkus Armbruster } 196d93bb9d5SMarkus Armbruster 197d93bb9d5SMarkus Armbruster test_name = g_strdup_printf("qmp/%s", si->name); 198d93bb9d5SMarkus Armbruster qtest_add_data_func(test_name, si->name, test_query); 199d93bb9d5SMarkus Armbruster g_free(test_name); 200d93bb9d5SMarkus Armbruster } 201d93bb9d5SMarkus Armbruster } 202d93bb9d5SMarkus Armbruster 203442b09b8SMarc-André Lureau static void test_object_add_without_props(void) 204442b09b8SMarc-André Lureau { 205442b09b8SMarc-André Lureau QTestState *qts; 206442b09b8SMarc-André Lureau QDict *resp; 207442b09b8SMarc-André Lureau 208442b09b8SMarc-André Lureau qts = qtest_init(common_args); 209442b09b8SMarc-André Lureau resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':" 210442b09b8SMarc-André Lureau " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }"); 211442b09b8SMarc-André Lureau g_assert_nonnull(resp); 212442b09b8SMarc-André Lureau qmp_assert_error_class(resp, "GenericError"); 213442b09b8SMarc-André Lureau qtest_quit(qts); 214442b09b8SMarc-André Lureau } 215442b09b8SMarc-André Lureau 216*9fc719b8SEric Auger static void test_object_add_with_duplicate_id(void) 217*9fc719b8SEric Auger { 218*9fc719b8SEric Auger QTestState *qts; 219*9fc719b8SEric Auger QDict *resp; 220*9fc719b8SEric Auger 221*9fc719b8SEric Auger qts = qtest_init(common_args); 222*9fc719b8SEric Auger resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':" 223*9fc719b8SEric Auger " {'qom-type': 'memory-backend-ram', 'id': 'ram1', 'props': {'size': 1048576 } } }"); 224*9fc719b8SEric Auger g_assert_nonnull(resp); 225*9fc719b8SEric Auger g_assert(qdict_haskey(resp, "return")); 226*9fc719b8SEric Auger resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':" 227*9fc719b8SEric Auger " {'qom-type': 'memory-backend-ram', 'id': 'ram1', 'props': {'size': 1048576 } } }"); 228*9fc719b8SEric Auger g_assert_nonnull(resp); 229*9fc719b8SEric Auger qmp_assert_error_class(resp, "GenericError"); 230*9fc719b8SEric Auger qtest_quit(qts); 231*9fc719b8SEric Auger } 232*9fc719b8SEric Auger 233d93bb9d5SMarkus Armbruster int main(int argc, char *argv[]) 234d93bb9d5SMarkus Armbruster { 235d93bb9d5SMarkus Armbruster QmpSchema schema; 236d93bb9d5SMarkus Armbruster int ret; 237d93bb9d5SMarkus Armbruster 238d93bb9d5SMarkus Armbruster g_test_init(&argc, &argv, NULL); 239d93bb9d5SMarkus Armbruster 240d93bb9d5SMarkus Armbruster qmp_schema_init(&schema); 241d93bb9d5SMarkus Armbruster add_query_tests(&schema); 242442b09b8SMarc-André Lureau 243442b09b8SMarc-André Lureau qtest_add_func("qmp/object-add-without-props", 244442b09b8SMarc-André Lureau test_object_add_without_props); 245*9fc719b8SEric Auger qtest_add_func("qmp/object-add-duplicate-id", 246*9fc719b8SEric Auger test_object_add_with_duplicate_id); 247442b09b8SMarc-André Lureau /* TODO: add coverage of generic object-add failure modes */ 248442b09b8SMarc-André Lureau 249d93bb9d5SMarkus Armbruster ret = g_test_run(); 250d93bb9d5SMarkus Armbruster 251d93bb9d5SMarkus Armbruster qmp_schema_cleanup(&schema); 252d93bb9d5SMarkus Armbruster return ret; 253d93bb9d5SMarkus Armbruster } 254