xref: /qemu/tests/unit/check-qom-proplist.c (revision a976a99a)
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 <berrange@redhat.com>
19  */
20 
21 #include "qemu/osdep.h"
22 
23 #include "qapi/error.h"
24 #include "qapi/qobject-input-visitor.h"
25 #include "qapi/qmp/qdict.h"
26 #include "qapi/qmp/qobject.h"
27 #include "qom/object.h"
28 #include "qemu/module.h"
29 #include "qemu/option.h"
30 #include "qemu/keyval.h"
31 #include "qemu/config-file.h"
32 #include "qom/object_interfaces.h"
33 
34 
35 #define TYPE_DUMMY "qemu-dummy"
36 
37 typedef struct DummyObject DummyObject;
38 typedef struct DummyObjectClass DummyObjectClass;
39 
40 DECLARE_INSTANCE_CHECKER(DummyObject, DUMMY_OBJECT,
41                          TYPE_DUMMY)
42 
43 typedef enum DummyAnimal DummyAnimal;
44 
45 enum DummyAnimal {
46     DUMMY_FROG,
47     DUMMY_ALLIGATOR,
48     DUMMY_PLATYPUS,
49 
50     DUMMY_LAST,
51 };
52 
53 const QEnumLookup dummy_animal_map = {
54     .array = (const char *const[]) {
55         [DUMMY_FROG] = "frog",
56         [DUMMY_ALLIGATOR] = "alligator",
57         [DUMMY_PLATYPUS] = "platypus",
58     },
59     .size = DUMMY_LAST
60 };
61 
62 struct DummyObject {
63     Object parent_obj;
64 
65     bool bv;
66     DummyAnimal av;
67     char *sv;
68 };
69 
70 struct DummyObjectClass {
71     ObjectClass parent_class;
72 };
73 
74 
75 static void dummy_set_bv(Object *obj,
76                          bool value,
77                          Error **errp)
78 {
79     DummyObject *dobj = DUMMY_OBJECT(obj);
80 
81     dobj->bv = value;
82 }
83 
84 static bool dummy_get_bv(Object *obj,
85                          Error **errp)
86 {
87     DummyObject *dobj = DUMMY_OBJECT(obj);
88 
89     return dobj->bv;
90 }
91 
92 
93 static void dummy_set_av(Object *obj,
94                          int value,
95                          Error **errp)
96 {
97     DummyObject *dobj = DUMMY_OBJECT(obj);
98 
99     dobj->av = value;
100 }
101 
102 static int dummy_get_av(Object *obj,
103                         Error **errp)
104 {
105     DummyObject *dobj = DUMMY_OBJECT(obj);
106 
107     return dobj->av;
108 }
109 
110 
111 static void dummy_set_sv(Object *obj,
112                          const char *value,
113                          Error **errp)
114 {
115     DummyObject *dobj = DUMMY_OBJECT(obj);
116 
117     g_free(dobj->sv);
118     dobj->sv = g_strdup(value);
119 }
120 
121 static char *dummy_get_sv(Object *obj,
122                           Error **errp)
123 {
124     DummyObject *dobj = DUMMY_OBJECT(obj);
125 
126     return g_strdup(dobj->sv);
127 }
128 
129 
130 static void dummy_init(Object *obj)
131 {
132     object_property_add_bool(obj, "bv",
133                              dummy_get_bv,
134                              dummy_set_bv);
135 }
136 
137 
138 static void dummy_class_init(ObjectClass *cls, void *data)
139 {
140     object_class_property_add_str(cls, "sv",
141                                   dummy_get_sv,
142                                   dummy_set_sv);
143     object_class_property_add_enum(cls, "av",
144                                    "DummyAnimal",
145                                    &dummy_animal_map,
146                                    dummy_get_av,
147                                    dummy_set_av);
148 }
149 
150 
151 static void dummy_finalize(Object *obj)
152 {
153     DummyObject *dobj = DUMMY_OBJECT(obj);
154 
155     g_free(dobj->sv);
156 }
157 
158 
159 static const TypeInfo dummy_info = {
160     .name          = TYPE_DUMMY,
161     .parent        = TYPE_OBJECT,
162     .instance_size = sizeof(DummyObject),
163     .instance_init = dummy_init,
164     .instance_finalize = dummy_finalize,
165     .class_size = sizeof(DummyObjectClass),
166     .class_init = dummy_class_init,
167     .interfaces = (InterfaceInfo[]) {
168         { TYPE_USER_CREATABLE },
169         { }
170     }
171 };
172 
173 
174 /*
175  * The following 3 object classes are used to
176  * simulate the kind of relationships seen in
177  * qdev, which result in complex object
178  * property destruction ordering.
179  *
180  * DummyDev has a 'bus' child to a DummyBus
181  * DummyBus has a 'backend' child to a DummyBackend
182  * DummyDev has a 'backend' link to DummyBackend
183  *
184  * When DummyDev is finalized, it unparents the
185  * DummyBackend, which unparents the DummyDev
186  * which deletes the 'backend' link from DummyDev
187  * to DummyBackend. This illustrates that the
188  * object_property_del_all() method needs to
189  * cope with the list of properties being changed
190  * while it iterates over them.
191  */
192 typedef struct DummyDev DummyDev;
193 typedef struct DummyDevClass DummyDevClass;
194 typedef struct DummyBus DummyBus;
195 typedef struct DummyBusClass DummyBusClass;
196 typedef struct DummyBackend DummyBackend;
197 typedef struct DummyBackendClass DummyBackendClass;
198 
199 #define TYPE_DUMMY_DEV "qemu-dummy-dev"
200 #define TYPE_DUMMY_BUS "qemu-dummy-bus"
201 #define TYPE_DUMMY_BACKEND "qemu-dummy-backend"
202 
203 DECLARE_INSTANCE_CHECKER(DummyDev, DUMMY_DEV,
204                          TYPE_DUMMY_DEV)
205 DECLARE_INSTANCE_CHECKER(DummyBus, DUMMY_BUS,
206                          TYPE_DUMMY_BUS)
207 DECLARE_INSTANCE_CHECKER(DummyBackend, DUMMY_BACKEND,
208                          TYPE_DUMMY_BACKEND)
209 
210 struct DummyDev {
211     Object parent_obj;
212 
213     DummyBus *bus;
214 };
215 
216 struct DummyDevClass {
217     ObjectClass parent_class;
218 };
219 
220 struct DummyBus {
221     Object parent_obj;
222 
223     DummyBackend *backend;
224 };
225 
226 struct DummyBusClass {
227     ObjectClass parent_class;
228 };
229 
230 struct DummyBackend {
231     Object parent_obj;
232 };
233 
234 struct DummyBackendClass {
235     ObjectClass parent_class;
236 };
237 
238 
239 static void dummy_dev_finalize(Object *obj)
240 {
241     DummyDev *dev = DUMMY_DEV(obj);
242 
243     object_unref(OBJECT(dev->bus));
244 }
245 
246 static void dummy_dev_init(Object *obj)
247 {
248     DummyDev *dev = DUMMY_DEV(obj);
249     DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS));
250     DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND));
251 
252     object_property_add_child(obj, "bus", OBJECT(bus));
253     dev->bus = bus;
254     object_property_add_child(OBJECT(bus), "backend", OBJECT(backend));
255     bus->backend = backend;
256 
257     object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND,
258                              (Object **)&bus->backend, NULL, 0);
259 }
260 
261 static void dummy_dev_unparent(Object *obj)
262 {
263     DummyDev *dev = DUMMY_DEV(obj);
264     object_unparent(OBJECT(dev->bus));
265 }
266 
267 static void dummy_dev_class_init(ObjectClass *klass, void *opaque)
268 {
269     klass->unparent = dummy_dev_unparent;
270 }
271 
272 
273 static void dummy_bus_finalize(Object *obj)
274 {
275     DummyBus *bus = DUMMY_BUS(obj);
276 
277     object_unref(OBJECT(bus->backend));
278 }
279 
280 static void dummy_bus_init(Object *obj)
281 {
282 }
283 
284 static void dummy_bus_unparent(Object *obj)
285 {
286     DummyBus *bus = DUMMY_BUS(obj);
287     object_property_del(obj->parent, "backend");
288     object_unparent(OBJECT(bus->backend));
289 }
290 
291 static void dummy_bus_class_init(ObjectClass *klass, void *opaque)
292 {
293     klass->unparent = dummy_bus_unparent;
294 }
295 
296 static void dummy_backend_init(Object *obj)
297 {
298 }
299 
300 
301 static const TypeInfo dummy_dev_info = {
302     .name          = TYPE_DUMMY_DEV,
303     .parent        = TYPE_OBJECT,
304     .instance_size = sizeof(DummyDev),
305     .instance_init = dummy_dev_init,
306     .instance_finalize = dummy_dev_finalize,
307     .class_size = sizeof(DummyDevClass),
308     .class_init = dummy_dev_class_init,
309 };
310 
311 static const TypeInfo dummy_bus_info = {
312     .name          = TYPE_DUMMY_BUS,
313     .parent        = TYPE_OBJECT,
314     .instance_size = sizeof(DummyBus),
315     .instance_init = dummy_bus_init,
316     .instance_finalize = dummy_bus_finalize,
317     .class_size = sizeof(DummyBusClass),
318     .class_init = dummy_bus_class_init,
319 };
320 
321 static const TypeInfo dummy_backend_info = {
322     .name          = TYPE_DUMMY_BACKEND,
323     .parent        = TYPE_OBJECT,
324     .instance_size = sizeof(DummyBackend),
325     .instance_init = dummy_backend_init,
326     .class_size = sizeof(DummyBackendClass),
327 };
328 
329 static QemuOptsList qemu_object_opts = {
330     .name = "object",
331     .implied_opt_name = "qom-type",
332     .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
333     .desc = {
334         { }
335     },
336 };
337 
338 
339 static void test_dummy_createv(void)
340 {
341     Error *err = NULL;
342     Object *parent = object_get_objects_root();
343     DummyObject *dobj = DUMMY_OBJECT(
344         object_new_with_props(TYPE_DUMMY,
345                               parent,
346                               "dummy0",
347                               &err,
348                               "bv", "yes",
349                               "sv", "Hiss hiss hiss",
350                               "av", "platypus",
351                               NULL));
352 
353     g_assert(err == NULL);
354     g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
355     g_assert(dobj->bv == true);
356     g_assert(dobj->av == DUMMY_PLATYPUS);
357 
358     g_assert(object_resolve_path_component(parent, "dummy0")
359              == OBJECT(dobj));
360 
361     object_unparent(OBJECT(dobj));
362 }
363 
364 
365 static Object *new_helper(Error **errp,
366                           Object *parent,
367                           ...)
368 {
369     va_list vargs;
370     Object *obj;
371 
372     va_start(vargs, parent);
373     obj = object_new_with_propv(TYPE_DUMMY,
374                                 parent,
375                                 "dummy0",
376                                 errp,
377                                 vargs);
378     va_end(vargs);
379     return obj;
380 }
381 
382 static void test_dummy_createlist(void)
383 {
384     Error *err = NULL;
385     Object *parent = object_get_objects_root();
386     DummyObject *dobj = DUMMY_OBJECT(
387         new_helper(&err,
388                    parent,
389                    "bv", "yes",
390                    "sv", "Hiss hiss hiss",
391                    "av", "platypus",
392                    NULL));
393 
394     g_assert(err == NULL);
395     g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
396     g_assert(dobj->bv == true);
397     g_assert(dobj->av == DUMMY_PLATYPUS);
398 
399     g_assert(object_resolve_path_component(parent, "dummy0")
400              == OBJECT(dobj));
401 
402     object_unparent(OBJECT(dobj));
403 }
404 
405 static bool test_create_obj(QDict *qdict, Error **errp)
406 {
407     Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
408     Object *obj = user_creatable_add_type(TYPE_DUMMY, "dev0", qdict, v, errp);
409 
410     visit_free(v);
411     object_unref(obj);
412     return !!obj;
413 }
414 
415 static void test_dummy_createcmdl(void)
416 {
417     QDict *qdict;
418     DummyObject *dobj;
419     Error *err = NULL;
420     bool created, help;
421     const char *params = "bv=yes,sv=Hiss hiss hiss,av=platypus";
422 
423     /* Needed for user_creatable_del.  */
424     qemu_add_opts(&qemu_object_opts);
425 
426     qdict = keyval_parse(params, "qom-type", &help, &err);
427     g_assert(err == NULL);
428     g_assert(qdict);
429     g_assert(!help);
430 
431     created = test_create_obj(qdict, &err);
432     g_assert(created);
433     g_assert(err == NULL);
434     qobject_unref(qdict);
435 
436     dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
437                                                       "dev0"));
438     g_assert(dobj);
439     g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
440     g_assert(dobj->bv == true);
441     g_assert(dobj->av == DUMMY_PLATYPUS);
442 
443     qdict = keyval_parse(params, "qom-type", &help, &err);
444     created = test_create_obj(qdict, &err);
445     g_assert(!created);
446     g_assert(err);
447     g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
448              == OBJECT(dobj));
449     qobject_unref(qdict);
450     error_free(err);
451     err = NULL;
452 
453     qdict = keyval_parse(params, "qom-type", &help, &err);
454     user_creatable_del("dev0", &error_abort);
455     g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
456              == NULL);
457 
458     created = test_create_obj(qdict, &err);
459     g_assert(created);
460     g_assert(err == NULL);
461     qobject_unref(qdict);
462 
463     dobj = DUMMY_OBJECT(object_resolve_path_component(object_get_objects_root(),
464                                                       "dev0"));
465     g_assert(dobj);
466     g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
467     g_assert(dobj->bv == true);
468     g_assert(dobj->av == DUMMY_PLATYPUS);
469     g_assert(object_resolve_path_component(object_get_objects_root(), "dev0")
470              == OBJECT(dobj));
471 
472     object_unparent(OBJECT(dobj));
473 }
474 
475 static void test_dummy_badenum(void)
476 {
477     Error *err = NULL;
478     Object *parent = object_get_objects_root();
479     Object *dobj =
480         object_new_with_props(TYPE_DUMMY,
481                               parent,
482                               "dummy0",
483                               &err,
484                               "bv", "yes",
485                               "sv", "Hiss hiss hiss",
486                               "av", "yeti",
487                               NULL);
488 
489     g_assert(dobj == NULL);
490     g_assert(err != NULL);
491     g_assert_cmpstr(error_get_pretty(err), ==,
492                     "Parameter 'av' does not accept value 'yeti'");
493 
494     g_assert(object_resolve_path_component(parent, "dummy0")
495              == NULL);
496 
497     error_free(err);
498 }
499 
500 
501 static void test_dummy_getenum(void)
502 {
503     Error *err = NULL;
504     int val;
505     Object *parent = object_get_objects_root();
506     DummyObject *dobj = DUMMY_OBJECT(
507         object_new_with_props(TYPE_DUMMY,
508                          parent,
509                          "dummy0",
510                          &err,
511                          "av", "platypus",
512                          NULL));
513 
514     g_assert(err == NULL);
515     g_assert(dobj->av == DUMMY_PLATYPUS);
516 
517     val = object_property_get_enum(OBJECT(dobj),
518                                    "av",
519                                    "DummyAnimal",
520                                    &error_abort);
521     g_assert(val == DUMMY_PLATYPUS);
522 
523     /* A bad enum type name */
524     val = object_property_get_enum(OBJECT(dobj),
525                                    "av",
526                                    "BadAnimal",
527                                    &err);
528     g_assert(val == -1);
529     error_free_or_abort(&err);
530 
531     /* A non-enum property name */
532     val = object_property_get_enum(OBJECT(dobj),
533                                    "iv",
534                                    "DummyAnimal",
535                                    &err);
536     g_assert(val == -1);
537     error_free_or_abort(&err);
538 
539     object_unparent(OBJECT(dobj));
540 }
541 
542 
543 static void test_dummy_prop_iterator(ObjectPropertyIterator *iter,
544                                      const char *expected[], int n)
545 {
546     ObjectProperty *prop;
547     int i;
548 
549     while ((prop = object_property_iter_next(iter))) {
550         for (i = 0; i < n; i++) {
551             if (!g_strcmp0(prop->name, expected[i])) {
552                 break;
553             }
554         }
555         g_assert(i < n);
556         expected[i] = NULL;
557     }
558 
559     for (i = 0; i < n; i++) {
560         g_assert(!expected[i]);
561     }
562 }
563 
564 static void test_dummy_iterator(void)
565 {
566     const char *expected[] = {
567         "type",                 /* inherited from TYPE_OBJECT */
568         "sv", "av",             /* class properties */
569         "bv"};                  /* instance property */
570     Object *parent = object_get_objects_root();
571     DummyObject *dobj = DUMMY_OBJECT(
572         object_new_with_props(TYPE_DUMMY,
573                               parent,
574                               "dummy0",
575                               &error_abort,
576                               "bv", "yes",
577                               "sv", "Hiss hiss hiss",
578                               "av", "platypus",
579                               NULL));
580     ObjectPropertyIterator iter;
581 
582     object_property_iter_init(&iter, OBJECT(dobj));
583     test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
584     object_unparent(OBJECT(dobj));
585 }
586 
587 static void test_dummy_class_iterator(void)
588 {
589     const char *expected[] = { "type", "av", "sv" };
590     ObjectPropertyIterator iter;
591     ObjectClass *klass = object_class_by_name(TYPE_DUMMY);
592 
593     object_class_property_iter_init(&iter, klass);
594     test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
595 }
596 
597 static void test_dummy_delchild(void)
598 {
599     Object *parent = object_get_objects_root();
600     DummyDev *dev = DUMMY_DEV(
601         object_new_with_props(TYPE_DUMMY_DEV,
602                               parent,
603                               "dev0",
604                               &error_abort,
605                               NULL));
606 
607     object_unparent(OBJECT(dev));
608 }
609 
610 static void test_qom_partial_path(void)
611 {
612     Object *root  = object_get_objects_root();
613     Object *cont1 = container_get(root, "/cont1");
614     Object *obj1  = object_new(TYPE_DUMMY);
615     Object *obj2a = object_new(TYPE_DUMMY);
616     Object *obj2b = object_new(TYPE_DUMMY);
617     bool ambiguous;
618 
619     /* Objects created:
620      * /cont1
621      * /cont1/obj1
622      * /cont1/obj2 (obj2a)
623      * /obj2 (obj2b)
624      */
625     object_property_add_child(cont1, "obj1", obj1);
626     object_unref(obj1);
627     object_property_add_child(cont1, "obj2", obj2a);
628     object_unref(obj2a);
629     object_property_add_child(root,  "obj2", obj2b);
630     object_unref(obj2b);
631 
632     ambiguous = false;
633     g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous));
634     g_assert(ambiguous);
635     g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL));
636 
637     ambiguous = false;
638     g_assert(!object_resolve_path("obj2", &ambiguous));
639     g_assert(ambiguous);
640     g_assert(!object_resolve_path("obj2", NULL));
641 
642     ambiguous = false;
643     g_assert(object_resolve_path("obj1", &ambiguous) == obj1);
644     g_assert(!ambiguous);
645     g_assert(object_resolve_path("obj1", NULL) == obj1);
646 
647     object_unparent(obj2b);
648     object_unparent(cont1);
649 }
650 
651 int main(int argc, char **argv)
652 {
653     g_test_init(&argc, &argv, NULL);
654 
655     module_call_init(MODULE_INIT_QOM);
656     type_register_static(&dummy_info);
657     type_register_static(&dummy_dev_info);
658     type_register_static(&dummy_bus_info);
659     type_register_static(&dummy_backend_info);
660 
661     g_test_add_func("/qom/proplist/createlist", test_dummy_createlist);
662     g_test_add_func("/qom/proplist/createv", test_dummy_createv);
663     g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl);
664     g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
665     g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
666     g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
667     g_test_add_func("/qom/proplist/class_iterator", test_dummy_class_iterator);
668     g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
669     g_test_add_func("/qom/resolve/partial", test_qom_partial_path);
670 
671     return g_test_run();
672 }
673