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