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