]>
Commit | Line | Data |
---|---|---|
a31bdae5 DB |
1 | /* |
2 | * Copyright (C) 2015 Red Hat, Inc. | |
3 | * | |
4 | * This library is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU Lesser General Public | |
6 | * License as published by the Free Software Foundation; either | |
7 | * version 2.1 of the License, or (at your option) any later version. | |
8 | * | |
9 | * This library is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * Lesser General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU Lesser General Public | |
15 | * License along with this library. If not, see | |
16 | * <http://www.gnu.org/licenses/>. | |
17 | * | |
18 | * Author: Daniel P. Berrange <[email protected]> | |
19 | */ | |
20 | ||
681c28a3 | 21 | #include "qemu/osdep.h" |
a31bdae5 | 22 | |
da34e65c | 23 | #include "qapi/error.h" |
a31bdae5 DB |
24 | #include "qom/object.h" |
25 | #include "qemu/module.h" | |
26 | ||
27 | ||
28 | #define TYPE_DUMMY "qemu-dummy" | |
29 | ||
30 | typedef struct DummyObject DummyObject; | |
31 | typedef struct DummyObjectClass DummyObjectClass; | |
32 | ||
33 | #define DUMMY_OBJECT(obj) \ | |
34 | OBJECT_CHECK(DummyObject, (obj), TYPE_DUMMY) | |
35 | ||
a8e3fbed DB |
36 | typedef enum DummyAnimal DummyAnimal; |
37 | ||
38 | enum DummyAnimal { | |
39 | DUMMY_FROG, | |
40 | DUMMY_ALLIGATOR, | |
41 | DUMMY_PLATYPUS, | |
42 | ||
43 | DUMMY_LAST, | |
44 | }; | |
45 | ||
46 | static const char *const dummy_animal_map[DUMMY_LAST + 1] = { | |
47 | [DUMMY_FROG] = "frog", | |
48 | [DUMMY_ALLIGATOR] = "alligator", | |
49 | [DUMMY_PLATYPUS] = "platypus", | |
50 | [DUMMY_LAST] = NULL, | |
51 | }; | |
52 | ||
a31bdae5 DB |
53 | struct DummyObject { |
54 | Object parent_obj; | |
55 | ||
56 | bool bv; | |
a8e3fbed | 57 | DummyAnimal av; |
a31bdae5 DB |
58 | char *sv; |
59 | }; | |
60 | ||
61 | struct DummyObjectClass { | |
62 | ObjectClass parent_class; | |
63 | }; | |
64 | ||
65 | ||
66 | static void dummy_set_bv(Object *obj, | |
67 | bool value, | |
68 | Error **errp) | |
69 | { | |
70 | DummyObject *dobj = DUMMY_OBJECT(obj); | |
71 | ||
72 | dobj->bv = value; | |
73 | } | |
74 | ||
75 | static bool dummy_get_bv(Object *obj, | |
76 | Error **errp) | |
77 | { | |
78 | DummyObject *dobj = DUMMY_OBJECT(obj); | |
79 | ||
80 | return dobj->bv; | |
81 | } | |
82 | ||
83 | ||
a8e3fbed DB |
84 | static void dummy_set_av(Object *obj, |
85 | int value, | |
86 | Error **errp) | |
87 | { | |
88 | DummyObject *dobj = DUMMY_OBJECT(obj); | |
89 | ||
90 | dobj->av = value; | |
91 | } | |
92 | ||
93 | static int dummy_get_av(Object *obj, | |
94 | Error **errp) | |
95 | { | |
96 | DummyObject *dobj = DUMMY_OBJECT(obj); | |
97 | ||
98 | return dobj->av; | |
99 | } | |
100 | ||
101 | ||
a31bdae5 DB |
102 | static void dummy_set_sv(Object *obj, |
103 | const char *value, | |
104 | Error **errp) | |
105 | { | |
106 | DummyObject *dobj = DUMMY_OBJECT(obj); | |
107 | ||
108 | g_free(dobj->sv); | |
109 | dobj->sv = g_strdup(value); | |
110 | } | |
111 | ||
112 | static char *dummy_get_sv(Object *obj, | |
113 | Error **errp) | |
114 | { | |
115 | DummyObject *dobj = DUMMY_OBJECT(obj); | |
116 | ||
117 | return g_strdup(dobj->sv); | |
118 | } | |
119 | ||
120 | ||
121 | static void dummy_init(Object *obj) | |
122 | { | |
123 | object_property_add_bool(obj, "bv", | |
124 | dummy_get_bv, | |
125 | dummy_set_bv, | |
126 | NULL); | |
a31bdae5 DB |
127 | } |
128 | ||
16bf7f52 DB |
129 | |
130 | static void dummy_class_init(ObjectClass *cls, void *data) | |
131 | { | |
132 | object_class_property_add_bool(cls, "bv", | |
133 | dummy_get_bv, | |
134 | dummy_set_bv, | |
135 | NULL); | |
136 | object_class_property_add_str(cls, "sv", | |
137 | dummy_get_sv, | |
138 | dummy_set_sv, | |
139 | NULL); | |
140 | object_class_property_add_enum(cls, "av", | |
141 | "DummyAnimal", | |
142 | dummy_animal_map, | |
143 | dummy_get_av, | |
144 | dummy_set_av, | |
145 | NULL); | |
146 | } | |
147 | ||
148 | ||
a31bdae5 DB |
149 | static void dummy_finalize(Object *obj) |
150 | { | |
151 | DummyObject *dobj = DUMMY_OBJECT(obj); | |
152 | ||
153 | g_free(dobj->sv); | |
154 | } | |
155 | ||
156 | ||
157 | static const TypeInfo dummy_info = { | |
158 | .name = TYPE_DUMMY, | |
159 | .parent = TYPE_OBJECT, | |
160 | .instance_size = sizeof(DummyObject), | |
161 | .instance_init = dummy_init, | |
162 | .instance_finalize = dummy_finalize, | |
163 | .class_size = sizeof(DummyObjectClass), | |
16bf7f52 | 164 | .class_init = dummy_class_init, |
a31bdae5 DB |
165 | }; |
166 | ||
8c4d156c DB |
167 | |
168 | /* | |
169 | * The following 3 object classes are used to | |
170 | * simulate the kind of relationships seen in | |
171 | * qdev, which result in complex object | |
172 | * property destruction ordering. | |
173 | * | |
174 | * DummyDev has a 'bus' child to a DummyBus | |
175 | * DummyBus has a 'backend' child to a DummyBackend | |
176 | * DummyDev has a 'backend' link to DummyBackend | |
177 | * | |
178 | * When DummyDev is finalized, it unparents the | |
179 | * DummyBackend, which unparents the DummyDev | |
180 | * which deletes the 'backend' link from DummyDev | |
181 | * to DummyBackend. This illustrates that the | |
182 | * object_property_del_all() method needs to | |
183 | * cope with the list of properties being changed | |
184 | * while it iterates over them. | |
185 | */ | |
186 | typedef struct DummyDev DummyDev; | |
187 | typedef struct DummyDevClass DummyDevClass; | |
188 | typedef struct DummyBus DummyBus; | |
189 | typedef struct DummyBusClass DummyBusClass; | |
190 | typedef struct DummyBackend DummyBackend; | |
191 | typedef struct DummyBackendClass DummyBackendClass; | |
192 | ||
193 | #define TYPE_DUMMY_DEV "qemu-dummy-dev" | |
194 | #define TYPE_DUMMY_BUS "qemu-dummy-bus" | |
195 | #define TYPE_DUMMY_BACKEND "qemu-dummy-backend" | |
196 | ||
197 | #define DUMMY_DEV(obj) \ | |
198 | OBJECT_CHECK(DummyDev, (obj), TYPE_DUMMY_DEV) | |
199 | #define DUMMY_BUS(obj) \ | |
200 | OBJECT_CHECK(DummyBus, (obj), TYPE_DUMMY_BUS) | |
201 | #define DUMMY_BACKEND(obj) \ | |
202 | OBJECT_CHECK(DummyBackend, (obj), TYPE_DUMMY_BACKEND) | |
203 | ||
204 | struct DummyDev { | |
205 | Object parent_obj; | |
206 | ||
207 | DummyBus *bus; | |
208 | }; | |
209 | ||
210 | struct DummyDevClass { | |
211 | ObjectClass parent_class; | |
212 | }; | |
213 | ||
214 | struct DummyBus { | |
215 | Object parent_obj; | |
216 | ||
217 | DummyBackend *backend; | |
218 | }; | |
219 | ||
220 | struct DummyBusClass { | |
221 | ObjectClass parent_class; | |
222 | }; | |
223 | ||
224 | struct DummyBackend { | |
225 | Object parent_obj; | |
226 | }; | |
227 | ||
228 | struct DummyBackendClass { | |
229 | ObjectClass parent_class; | |
230 | }; | |
231 | ||
232 | ||
233 | static void dummy_dev_init(Object *obj) | |
234 | { | |
235 | DummyDev *dev = DUMMY_DEV(obj); | |
236 | DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS)); | |
237 | DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND)); | |
238 | ||
239 | object_property_add_child(obj, "bus", OBJECT(bus), NULL); | |
240 | dev->bus = bus; | |
241 | object_property_add_child(OBJECT(bus), "backend", OBJECT(backend), NULL); | |
242 | bus->backend = backend; | |
243 | ||
244 | object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND, | |
245 | (Object **)&bus->backend, NULL, 0, NULL); | |
246 | } | |
247 | ||
248 | static void dummy_dev_unparent(Object *obj) | |
249 | { | |
250 | DummyDev *dev = DUMMY_DEV(obj); | |
251 | object_unparent(OBJECT(dev->bus)); | |
252 | } | |
253 | ||
254 | static void dummy_dev_class_init(ObjectClass *klass, void *opaque) | |
255 | { | |
256 | klass->unparent = dummy_dev_unparent; | |
257 | } | |
258 | ||
259 | ||
260 | static void dummy_bus_init(Object *obj) | |
261 | { | |
262 | } | |
263 | ||
264 | static void dummy_bus_unparent(Object *obj) | |
265 | { | |
266 | DummyBus *bus = DUMMY_BUS(obj); | |
267 | object_property_del(obj->parent, "backend", NULL); | |
268 | object_unparent(OBJECT(bus->backend)); | |
269 | } | |
270 | ||
271 | static void dummy_bus_class_init(ObjectClass *klass, void *opaque) | |
272 | { | |
273 | klass->unparent = dummy_bus_unparent; | |
274 | } | |
275 | ||
276 | static void dummy_backend_init(Object *obj) | |
277 | { | |
278 | } | |
279 | ||
280 | ||
281 | static const TypeInfo dummy_dev_info = { | |
282 | .name = TYPE_DUMMY_DEV, | |
283 | .parent = TYPE_OBJECT, | |
284 | .instance_size = sizeof(DummyDev), | |
285 | .instance_init = dummy_dev_init, | |
286 | .class_size = sizeof(DummyDevClass), | |
287 | .class_init = dummy_dev_class_init, | |
288 | }; | |
289 | ||
290 | static const TypeInfo dummy_bus_info = { | |
291 | .name = TYPE_DUMMY_BUS, | |
292 | .parent = TYPE_OBJECT, | |
293 | .instance_size = sizeof(DummyBus), | |
294 | .instance_init = dummy_bus_init, | |
295 | .class_size = sizeof(DummyBusClass), | |
296 | .class_init = dummy_bus_class_init, | |
297 | }; | |
298 | ||
299 | static const TypeInfo dummy_backend_info = { | |
300 | .name = TYPE_DUMMY_BACKEND, | |
301 | .parent = TYPE_OBJECT, | |
302 | .instance_size = sizeof(DummyBackend), | |
303 | .instance_init = dummy_backend_init, | |
304 | .class_size = sizeof(DummyBackendClass), | |
305 | }; | |
306 | ||
307 | ||
308 | ||
a31bdae5 DB |
309 | static void test_dummy_createv(void) |
310 | { | |
311 | Error *err = NULL; | |
312 | Object *parent = object_get_objects_root(); | |
313 | DummyObject *dobj = DUMMY_OBJECT( | |
314 | object_new_with_props(TYPE_DUMMY, | |
315 | parent, | |
316 | "dummy0", | |
317 | &err, | |
318 | "bv", "yes", | |
319 | "sv", "Hiss hiss hiss", | |
a8e3fbed | 320 | "av", "platypus", |
a31bdae5 DB |
321 | NULL)); |
322 | ||
323 | g_assert(err == NULL); | |
324 | g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); | |
325 | g_assert(dobj->bv == true); | |
a8e3fbed | 326 | g_assert(dobj->av == DUMMY_PLATYPUS); |
a31bdae5 DB |
327 | |
328 | g_assert(object_resolve_path_component(parent, "dummy0") | |
329 | == OBJECT(dobj)); | |
330 | ||
331 | object_unparent(OBJECT(dobj)); | |
332 | } | |
333 | ||
334 | ||
335 | static Object *new_helper(Error **errp, | |
336 | Object *parent, | |
337 | ...) | |
338 | { | |
339 | va_list vargs; | |
340 | Object *obj; | |
341 | ||
342 | va_start(vargs, parent); | |
343 | obj = object_new_with_propv(TYPE_DUMMY, | |
344 | parent, | |
345 | "dummy0", | |
346 | errp, | |
347 | vargs); | |
348 | va_end(vargs); | |
349 | return obj; | |
350 | } | |
351 | ||
352 | static void test_dummy_createlist(void) | |
353 | { | |
354 | Error *err = NULL; | |
355 | Object *parent = object_get_objects_root(); | |
356 | DummyObject *dobj = DUMMY_OBJECT( | |
357 | new_helper(&err, | |
358 | parent, | |
359 | "bv", "yes", | |
360 | "sv", "Hiss hiss hiss", | |
a8e3fbed | 361 | "av", "platypus", |
a31bdae5 DB |
362 | NULL)); |
363 | ||
364 | g_assert(err == NULL); | |
365 | g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss"); | |
366 | g_assert(dobj->bv == true); | |
a8e3fbed | 367 | g_assert(dobj->av == DUMMY_PLATYPUS); |
a31bdae5 DB |
368 | |
369 | g_assert(object_resolve_path_component(parent, "dummy0") | |
370 | == OBJECT(dobj)); | |
371 | ||
372 | object_unparent(OBJECT(dobj)); | |
373 | } | |
374 | ||
a8e3fbed DB |
375 | static void test_dummy_badenum(void) |
376 | { | |
377 | Error *err = NULL; | |
378 | Object *parent = object_get_objects_root(); | |
379 | Object *dobj = | |
380 | object_new_with_props(TYPE_DUMMY, | |
381 | parent, | |
382 | "dummy0", | |
383 | &err, | |
384 | "bv", "yes", | |
385 | "sv", "Hiss hiss hiss", | |
386 | "av", "yeti", | |
387 | NULL); | |
388 | ||
389 | g_assert(dobj == NULL); | |
390 | g_assert(err != NULL); | |
391 | g_assert_cmpstr(error_get_pretty(err), ==, | |
392 | "Invalid parameter 'yeti'"); | |
393 | ||
394 | g_assert(object_resolve_path_component(parent, "dummy0") | |
395 | == NULL); | |
396 | ||
397 | error_free(err); | |
398 | } | |
399 | ||
400 | ||
a3590dac DB |
401 | static void test_dummy_getenum(void) |
402 | { | |
403 | Error *err = NULL; | |
404 | int val; | |
405 | Object *parent = object_get_objects_root(); | |
406 | DummyObject *dobj = DUMMY_OBJECT( | |
407 | object_new_with_props(TYPE_DUMMY, | |
408 | parent, | |
409 | "dummy0", | |
410 | &err, | |
411 | "av", "platypus", | |
412 | NULL)); | |
413 | ||
414 | g_assert(err == NULL); | |
415 | g_assert(dobj->av == DUMMY_PLATYPUS); | |
416 | ||
417 | val = object_property_get_enum(OBJECT(dobj), | |
418 | "av", | |
419 | "DummyAnimal", | |
420 | &err); | |
421 | g_assert(err == NULL); | |
422 | g_assert(val == DUMMY_PLATYPUS); | |
423 | ||
424 | /* A bad enum type name */ | |
425 | val = object_property_get_enum(OBJECT(dobj), | |
426 | "av", | |
427 | "BadAnimal", | |
428 | &err); | |
429 | g_assert(err != NULL); | |
430 | error_free(err); | |
431 | err = NULL; | |
432 | ||
433 | /* A non-enum property name */ | |
434 | val = object_property_get_enum(OBJECT(dobj), | |
435 | "iv", | |
436 | "DummyAnimal", | |
437 | &err); | |
438 | g_assert(err != NULL); | |
439 | error_free(err); | |
a00c9482 DB |
440 | |
441 | object_unparent(OBJECT(dobj)); | |
442 | } | |
443 | ||
444 | ||
445 | static void test_dummy_iterator(void) | |
446 | { | |
447 | Object *parent = object_get_objects_root(); | |
448 | DummyObject *dobj = DUMMY_OBJECT( | |
449 | object_new_with_props(TYPE_DUMMY, | |
450 | parent, | |
451 | "dummy0", | |
452 | &error_abort, | |
453 | "bv", "yes", | |
454 | "sv", "Hiss hiss hiss", | |
455 | "av", "platypus", | |
456 | NULL)); | |
457 | ||
458 | ObjectProperty *prop; | |
7746abd8 | 459 | ObjectPropertyIterator iter; |
a00c9482 DB |
460 | bool seenbv = false, seensv = false, seenav = false, seentype; |
461 | ||
7746abd8 DB |
462 | object_property_iter_init(&iter, OBJECT(dobj)); |
463 | while ((prop = object_property_iter_next(&iter))) { | |
a00c9482 DB |
464 | if (g_str_equal(prop->name, "bv")) { |
465 | seenbv = true; | |
466 | } else if (g_str_equal(prop->name, "sv")) { | |
467 | seensv = true; | |
468 | } else if (g_str_equal(prop->name, "av")) { | |
469 | seenav = true; | |
470 | } else if (g_str_equal(prop->name, "type")) { | |
471 | /* This prop comes from the base Object class */ | |
472 | seentype = true; | |
473 | } else { | |
474 | g_printerr("Found prop '%s'\n", prop->name); | |
475 | g_assert_not_reached(); | |
476 | } | |
477 | } | |
a00c9482 DB |
478 | g_assert(seenbv); |
479 | g_assert(seenav); | |
480 | g_assert(seensv); | |
481 | g_assert(seentype); | |
482 | ||
483 | object_unparent(OBJECT(dobj)); | |
a3590dac DB |
484 | } |
485 | ||
486 | ||
8c4d156c DB |
487 | static void test_dummy_delchild(void) |
488 | { | |
489 | Object *parent = object_get_objects_root(); | |
490 | DummyDev *dev = DUMMY_DEV( | |
491 | object_new_with_props(TYPE_DUMMY_DEV, | |
492 | parent, | |
493 | "dev0", | |
494 | &error_abort, | |
495 | NULL)); | |
496 | ||
497 | object_unparent(OBJECT(dev)); | |
498 | } | |
499 | ||
a31bdae5 DB |
500 | int main(int argc, char **argv) |
501 | { | |
502 | g_test_init(&argc, &argv, NULL); | |
503 | ||
504 | module_call_init(MODULE_INIT_QOM); | |
505 | type_register_static(&dummy_info); | |
8c4d156c DB |
506 | type_register_static(&dummy_dev_info); |
507 | type_register_static(&dummy_bus_info); | |
508 | type_register_static(&dummy_backend_info); | |
a31bdae5 DB |
509 | |
510 | g_test_add_func("/qom/proplist/createlist", test_dummy_createlist); | |
511 | g_test_add_func("/qom/proplist/createv", test_dummy_createv); | |
a8e3fbed | 512 | g_test_add_func("/qom/proplist/badenum", test_dummy_badenum); |
a3590dac | 513 | g_test_add_func("/qom/proplist/getenum", test_dummy_getenum); |
a00c9482 | 514 | g_test_add_func("/qom/proplist/iterator", test_dummy_iterator); |
8c4d156c | 515 | g_test_add_func("/qom/proplist/delchild", test_dummy_delchild); |
a31bdae5 DB |
516 | |
517 | return g_test_run(); | |
518 | } |