]>
Commit | Line | Data |
---|---|---|
2d1abb85 MA |
1 | /* |
2 | * Device introspection test cases | |
3 | * | |
4 | * Copyright (c) 2015 Red Hat Inc. | |
5 | * | |
6 | * Authors: | |
7 | * Markus Armbruster <[email protected]>, | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
13 | /* | |
14 | * Covers QMP device-list-properties and HMP device_add help. We | |
15 | * currently don't check that their output makes sense, only that QEMU | |
16 | * survives. Useful since we've had an astounding number of crash | |
17 | * bugs around here. | |
18 | */ | |
19 | ||
681c28a3 | 20 | #include "qemu/osdep.h" |
2d1abb85 MA |
21 | #include "qemu-common.h" |
22 | #include "qapi/qmp/qstring.h" | |
1c6d75d5 | 23 | #include "qapi/qmp/qdict.h" |
47e6b297 | 24 | #include "qapi/qmp/qlist.h" |
2d1abb85 MA |
25 | #include "libqtest.h" |
26 | ||
27 | const char common_args[] = "-nodefaults -machine none"; | |
28 | ||
39fb795a TH |
29 | static QList *qom_list_types(QTestState * qts, const char *implements, |
30 | bool abstract) | |
2d1abb85 MA |
31 | { |
32 | QDict *resp; | |
33 | QList *ret; | |
1c6d75d5 | 34 | QDict *args = qdict_new(); |
2d1abb85 | 35 | |
46f5ac20 | 36 | qdict_put_bool(args, "abstract", abstract); |
1c6d75d5 | 37 | if (implements) { |
46f5ac20 | 38 | qdict_put_str(args, "implements", implements); |
1c6d75d5 | 39 | } |
39fb795a TH |
40 | resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", |
41 | args); | |
2d1abb85 MA |
42 | g_assert(qdict_haskey(resp, "return")); |
43 | ret = qdict_get_qlist(resp, "return"); | |
cb3e7f08 MAL |
44 | qobject_ref(ret); |
45 | qobject_unref(resp); | |
2d1abb85 MA |
46 | return ret; |
47 | } | |
48 | ||
f86285c5 EH |
49 | /* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */ |
50 | static QDict *qom_type_index(QList *types) | |
51 | { | |
52 | QDict *index = qdict_new(); | |
53 | QListEntry *e; | |
54 | ||
55 | QLIST_FOREACH_ENTRY(types, e) { | |
7dc847eb | 56 | QDict *d = qobject_to(QDict, qlist_entry_obj(e)); |
f86285c5 | 57 | const char *name = qdict_get_str(d, "name"); |
cb3e7f08 | 58 | qobject_ref(d); |
f86285c5 EH |
59 | qdict_put(index, name, d); |
60 | } | |
61 | return index; | |
62 | } | |
63 | ||
64 | /* Check if @parent is present in the parent chain of @type */ | |
65 | static bool qom_has_parent(QDict *index, const char *type, const char *parent) | |
66 | { | |
67 | while (type) { | |
68 | QDict *d = qdict_get_qdict(index, type); | |
69 | const char *p = d && qdict_haskey(d, "parent") ? | |
70 | qdict_get_str(d, "parent") : | |
71 | NULL; | |
72 | ||
73 | if (!strcmp(type, parent)) { | |
74 | return true; | |
75 | } | |
76 | ||
77 | type = p; | |
78 | } | |
79 | ||
80 | return false; | |
81 | } | |
82 | ||
dbb2a604 EH |
83 | /* Find an entry on a list returned by qom-list-types */ |
84 | static QDict *type_list_find(QList *types, const char *name) | |
85 | { | |
86 | QListEntry *e; | |
87 | ||
88 | QLIST_FOREACH_ENTRY(types, e) { | |
7dc847eb | 89 | QDict *d = qobject_to(QDict, qlist_entry_obj(e)); |
dbb2a604 EH |
90 | const char *ename = qdict_get_str(d, "name"); |
91 | if (!strcmp(ename, name)) { | |
92 | return d; | |
93 | } | |
94 | } | |
95 | ||
96 | return NULL; | |
97 | } | |
98 | ||
39fb795a | 99 | static QList *device_type_list(QTestState *qts, bool abstract) |
1c6d75d5 | 100 | { |
39fb795a | 101 | return qom_list_types(qts, "device", abstract); |
1c6d75d5 EH |
102 | } |
103 | ||
39fb795a | 104 | static void test_one_device(QTestState *qts, const char *type) |
2d1abb85 MA |
105 | { |
106 | QDict *resp; | |
d0685212 TH |
107 | char *help; |
108 | char *qom_tree_start, *qom_tree_end; | |
109 | char *qtree_start, *qtree_end; | |
110 | ||
111 | g_test_message("Testing device '%s'", type); | |
112 | ||
39fb795a TH |
113 | qom_tree_start = qtest_hmp(qts, "info qom-tree"); |
114 | qtree_start = qtest_hmp(qts, "info qtree"); | |
2d1abb85 | 115 | |
39fb795a TH |
116 | resp = qtest_qmp(qts, "{'execute': 'device-list-properties'," |
117 | " 'arguments': {'typename': %s}}", | |
edb1523d | 118 | type); |
cb3e7f08 | 119 | qobject_unref(resp); |
2d1abb85 | 120 | |
39fb795a | 121 | help = qtest_hmp(qts, "device_add \"%s,help\"", type); |
2d1abb85 MA |
122 | g_free(help); |
123 | ||
124 | /* | |
125 | * Some devices leave dangling pointers in QOM behind. | |
d0685212 TH |
126 | * "info qom-tree" or "info qtree" have a good chance at crashing then. |
127 | * Also make sure that the tree did not change. | |
2d1abb85 | 128 | */ |
39fb795a | 129 | qom_tree_end = qtest_hmp(qts, "info qom-tree"); |
d0685212 TH |
130 | g_assert_cmpstr(qom_tree_start, ==, qom_tree_end); |
131 | g_free(qom_tree_start); | |
132 | g_free(qom_tree_end); | |
133 | ||
39fb795a | 134 | qtree_end = qtest_hmp(qts, "info qtree"); |
d0685212 TH |
135 | g_assert_cmpstr(qtree_start, ==, qtree_end); |
136 | g_free(qtree_start); | |
137 | g_free(qtree_end); | |
2d1abb85 MA |
138 | } |
139 | ||
140 | static void test_device_intro_list(void) | |
141 | { | |
142 | QList *types; | |
143 | char *help; | |
39fb795a | 144 | QTestState *qts; |
2d1abb85 | 145 | |
39fb795a | 146 | qts = qtest_init(common_args); |
2d1abb85 | 147 | |
39fb795a | 148 | types = device_type_list(qts, true); |
cb3e7f08 | 149 | qobject_unref(types); |
2d1abb85 | 150 | |
39fb795a | 151 | help = qtest_hmp(qts, "device_add help"); |
2d1abb85 MA |
152 | g_free(help); |
153 | ||
39fb795a | 154 | qtest_quit(qts); |
2d1abb85 MA |
155 | } |
156 | ||
f86285c5 EH |
157 | /* |
158 | * Ensure all entries returned by qom-list-types implements=<parent> | |
159 | * have <parent> as a parent. | |
160 | */ | |
39fb795a | 161 | static void test_qom_list_parents(QTestState *qts, const char *parent) |
f86285c5 EH |
162 | { |
163 | QList *types; | |
164 | QListEntry *e; | |
165 | QDict *index; | |
166 | ||
39fb795a | 167 | types = qom_list_types(qts, parent, true); |
f86285c5 EH |
168 | index = qom_type_index(types); |
169 | ||
170 | QLIST_FOREACH_ENTRY(types, e) { | |
7dc847eb | 171 | QDict *d = qobject_to(QDict, qlist_entry_obj(e)); |
f86285c5 EH |
172 | const char *name = qdict_get_str(d, "name"); |
173 | ||
174 | g_assert(qom_has_parent(index, name, parent)); | |
175 | } | |
176 | ||
cb3e7f08 MAL |
177 | qobject_unref(types); |
178 | qobject_unref(index); | |
f86285c5 EH |
179 | } |
180 | ||
87467eae EH |
181 | static void test_qom_list_fields(void) |
182 | { | |
183 | QList *all_types; | |
184 | QList *non_abstract; | |
185 | QListEntry *e; | |
39fb795a | 186 | QTestState *qts; |
87467eae | 187 | |
39fb795a | 188 | qts = qtest_init(common_args); |
87467eae | 189 | |
39fb795a TH |
190 | all_types = qom_list_types(qts, NULL, true); |
191 | non_abstract = qom_list_types(qts, NULL, false); | |
87467eae EH |
192 | |
193 | QLIST_FOREACH_ENTRY(all_types, e) { | |
7dc847eb | 194 | QDict *d = qobject_to(QDict, qlist_entry_obj(e)); |
87467eae EH |
195 | const char *name = qdict_get_str(d, "name"); |
196 | bool abstract = qdict_haskey(d, "abstract") ? | |
197 | qdict_get_bool(d, "abstract") : | |
198 | false; | |
199 | bool expected_abstract = !type_list_find(non_abstract, name); | |
200 | ||
201 | g_assert(abstract == expected_abstract); | |
202 | } | |
203 | ||
39fb795a TH |
204 | test_qom_list_parents(qts, "object"); |
205 | test_qom_list_parents(qts, "device"); | |
206 | test_qom_list_parents(qts, "sys-bus-device"); | |
f86285c5 | 207 | |
cb3e7f08 MAL |
208 | qobject_unref(all_types); |
209 | qobject_unref(non_abstract); | |
39fb795a | 210 | qtest_quit(qts); |
87467eae EH |
211 | } |
212 | ||
2d1abb85 MA |
213 | static void test_device_intro_none(void) |
214 | { | |
39fb795a TH |
215 | QTestState *qts = qtest_init(common_args); |
216 | ||
217 | test_one_device(qts, "nonexistent"); | |
218 | qtest_quit(qts); | |
2d1abb85 MA |
219 | } |
220 | ||
221 | static void test_device_intro_abstract(void) | |
222 | { | |
39fb795a TH |
223 | QTestState *qts = qtest_init(common_args); |
224 | ||
225 | test_one_device(qts, "device"); | |
226 | qtest_quit(qts); | |
2d1abb85 MA |
227 | } |
228 | ||
410573aa | 229 | static void test_device_intro_concrete(const void *args) |
2d1abb85 MA |
230 | { |
231 | QList *types; | |
232 | QListEntry *entry; | |
233 | const char *type; | |
39fb795a | 234 | QTestState *qts; |
2d1abb85 | 235 | |
39fb795a TH |
236 | qts = qtest_init(args); |
237 | types = device_type_list(qts, false); | |
2d1abb85 MA |
238 | |
239 | QLIST_FOREACH_ENTRY(types, entry) { | |
7dc847eb HR |
240 | type = qdict_get_try_str(qobject_to(QDict, qlist_entry_obj(entry)), |
241 | "name"); | |
2d1abb85 | 242 | g_assert(type); |
39fb795a | 243 | test_one_device(qts, type); |
2d1abb85 MA |
244 | } |
245 | ||
cb3e7f08 | 246 | qobject_unref(types); |
39fb795a | 247 | qtest_quit(qts); |
410573aa | 248 | g_free((void *)args); |
2d1abb85 MA |
249 | } |
250 | ||
1c6d75d5 EH |
251 | static void test_abstract_interfaces(void) |
252 | { | |
253 | QList *all_types; | |
dbb2a604 | 254 | QListEntry *e; |
f86285c5 | 255 | QDict *index; |
39fb795a | 256 | QTestState *qts; |
1c6d75d5 | 257 | |
39fb795a | 258 | qts = qtest_init(common_args); |
f86285c5 | 259 | |
39fb795a | 260 | all_types = qom_list_types(qts, "interface", true); |
f86285c5 | 261 | index = qom_type_index(all_types); |
1c6d75d5 | 262 | |
dbb2a604 | 263 | QLIST_FOREACH_ENTRY(all_types, e) { |
7dc847eb | 264 | QDict *d = qobject_to(QDict, qlist_entry_obj(e)); |
f86285c5 | 265 | const char *name = qdict_get_str(d, "name"); |
dbb2a604 | 266 | |
f86285c5 EH |
267 | /* |
268 | * qom-list-types implements=interface returns all types | |
269 | * that implement _any_ interface (not just interface | |
270 | * types), so skip the ones that don't have "interface" | |
271 | * on the parent type chain. | |
272 | */ | |
273 | if (!qom_has_parent(index, name, "interface")) { | |
87467eae EH |
274 | /* Not an interface type */ |
275 | continue; | |
276 | } | |
1c6d75d5 | 277 | |
87467eae | 278 | g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); |
1c6d75d5 EH |
279 | } |
280 | ||
cb3e7f08 MAL |
281 | qobject_unref(all_types); |
282 | qobject_unref(index); | |
39fb795a | 283 | qtest_quit(qts); |
1c6d75d5 EH |
284 | } |
285 | ||
410573aa TH |
286 | static void add_machine_test_case(const char *mname) |
287 | { | |
288 | char *path, *args; | |
289 | ||
290 | /* Ignore blacklisted machines */ | |
291 | if (g_str_equal("xenfv", mname) || g_str_equal("xenpv", mname)) { | |
292 | return; | |
293 | } | |
294 | ||
295 | path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); | |
296 | args = g_strdup_printf("-M %s", mname); | |
297 | qtest_add_data_func(path, args, test_device_intro_concrete); | |
298 | g_free(path); | |
299 | ||
300 | path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); | |
301 | args = g_strdup_printf("-nodefaults -M %s", mname); | |
302 | qtest_add_data_func(path, args, test_device_intro_concrete); | |
303 | g_free(path); | |
304 | } | |
305 | ||
2d1abb85 MA |
306 | int main(int argc, char **argv) |
307 | { | |
308 | g_test_init(&argc, &argv, NULL); | |
309 | ||
310 | qtest_add_func("device/introspect/list", test_device_intro_list); | |
87467eae | 311 | qtest_add_func("device/introspect/list-fields", test_qom_list_fields); |
2d1abb85 MA |
312 | qtest_add_func("device/introspect/none", test_device_intro_none); |
313 | qtest_add_func("device/introspect/abstract", test_device_intro_abstract); | |
1c6d75d5 | 314 | qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); |
410573aa TH |
315 | if (g_test_quick()) { |
316 | qtest_add_data_func("device/introspect/concrete/defaults/none", | |
317 | g_strdup(common_args), test_device_intro_concrete); | |
318 | } else { | |
319 | qtest_cb_for_every_machine(add_machine_test_case, true); | |
320 | } | |
2d1abb85 MA |
321 | |
322 | return g_test_run(); | |
323 | } |