check-qom-proplist.c (18285B)
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 36typedef struct DummyObject DummyObject; 37typedef struct DummyObjectClass DummyObjectClass; 38 39DECLARE_INSTANCE_CHECKER(DummyObject, DUMMY_OBJECT, 40 TYPE_DUMMY) 41 42typedef enum DummyAnimal DummyAnimal; 43 44enum DummyAnimal { 45 DUMMY_FROG, 46 DUMMY_ALLIGATOR, 47 DUMMY_PLATYPUS, 48 49 DUMMY_LAST, 50}; 51 52const 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 61struct DummyObject { 62 Object parent_obj; 63 64 bool bv; 65 DummyAnimal av; 66 char *sv; 67}; 68 69struct DummyObjectClass { 70 ObjectClass parent_class; 71}; 72 73 74static 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 83static bool dummy_get_bv(Object *obj, 84 Error **errp) 85{ 86 DummyObject *dobj = DUMMY_OBJECT(obj); 87 88 return dobj->bv; 89} 90 91 92static 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 101static int dummy_get_av(Object *obj, 102 Error **errp) 103{ 104 DummyObject *dobj = DUMMY_OBJECT(obj); 105 106 return dobj->av; 107} 108 109 110static 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 120static 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 129static void dummy_init(Object *obj) 130{ 131 object_property_add_bool(obj, "bv", 132 dummy_get_bv, 133 dummy_set_bv); 134} 135 136 137static 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 150static void dummy_finalize(Object *obj) 151{ 152 DummyObject *dobj = DUMMY_OBJECT(obj); 153 154 g_free(dobj->sv); 155} 156 157 158static 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 */ 191typedef struct DummyDev DummyDev; 192typedef struct DummyDevClass DummyDevClass; 193typedef struct DummyBus DummyBus; 194typedef struct DummyBusClass DummyBusClass; 195typedef struct DummyBackend DummyBackend; 196typedef 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 202DECLARE_INSTANCE_CHECKER(DummyDev, DUMMY_DEV, 203 TYPE_DUMMY_DEV) 204DECLARE_INSTANCE_CHECKER(DummyBus, DUMMY_BUS, 205 TYPE_DUMMY_BUS) 206DECLARE_INSTANCE_CHECKER(DummyBackend, DUMMY_BACKEND, 207 TYPE_DUMMY_BACKEND) 208 209struct DummyDev { 210 Object parent_obj; 211 212 DummyBus *bus; 213}; 214 215struct DummyDevClass { 216 ObjectClass parent_class; 217}; 218 219struct DummyBus { 220 Object parent_obj; 221 222 DummyBackend *backend; 223}; 224 225struct DummyBusClass { 226 ObjectClass parent_class; 227}; 228 229struct DummyBackend { 230 Object parent_obj; 231}; 232 233struct DummyBackendClass { 234 ObjectClass parent_class; 235}; 236 237 238static void dummy_dev_finalize(Object *obj) 239{ 240 DummyDev *dev = DUMMY_DEV(obj); 241 242 object_unref(OBJECT(dev->bus)); 243} 244 245static 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 260static void dummy_dev_unparent(Object *obj) 261{ 262 DummyDev *dev = DUMMY_DEV(obj); 263 object_unparent(OBJECT(dev->bus)); 264} 265 266static void dummy_dev_class_init(ObjectClass *klass, void *opaque) 267{ 268 klass->unparent = dummy_dev_unparent; 269} 270 271 272static void dummy_bus_finalize(Object *obj) 273{ 274 DummyBus *bus = DUMMY_BUS(obj); 275 276 object_unref(OBJECT(bus->backend)); 277} 278 279static void dummy_bus_init(Object *obj) 280{ 281} 282 283static 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 290static void dummy_bus_class_init(ObjectClass *klass, void *opaque) 291{ 292 klass->unparent = dummy_bus_unparent; 293} 294 295static void dummy_backend_init(Object *obj) 296{ 297} 298 299 300static 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 310static 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 320static 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 328static 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 338static 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 364static 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 381static 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 404static 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 414static 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 474static 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 500static 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 542static 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 563static 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 586static 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 596static 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 609static 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 650int 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}