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