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