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
  36DECLARE_INSTANCE_CHECKER(DummyObject, DUMMY_OBJECT,
  37                         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    object_property_add_bool(obj, "bv",
 129                             dummy_get_bv,
 130                             dummy_set_bv);
 131}
 132
 133
 134static void dummy_class_init(ObjectClass *cls, void *data)
 135{
 136    object_class_property_add_str(cls, "sv",
 137                                  dummy_get_sv,
 138                                  dummy_set_sv);
 139    object_class_property_add_enum(cls, "av",
 140                                   "DummyAnimal",
 141                                   &dummy_animal_map,
 142                                   dummy_get_av,
 143                                   dummy_set_av);
 144}
 145
 146
 147static void dummy_finalize(Object *obj)
 148{
 149    DummyObject *dobj = DUMMY_OBJECT(obj);
 150
 151    g_free(dobj->sv);
 152}
 153
 154
 155static const TypeInfo dummy_info = {
 156    .name          = TYPE_DUMMY,
 157    .parent        = TYPE_OBJECT,
 158    .instance_size = sizeof(DummyObject),
 159    .instance_init = dummy_init,
 160    .instance_finalize = dummy_finalize,
 161    .class_size = sizeof(DummyObjectClass),
 162    .class_init = dummy_class_init,
 163    .interfaces = (InterfaceInfo[]) {
 164        { TYPE_USER_CREATABLE },
 165        { }
 166    }
 167};
 168
 169
 170/*
 171 * The following 3 object classes are used to
 172 * simulate the kind of relationships seen in
 173 * qdev, which result in complex object
 174 * property destruction ordering.
 175 *
 176 * DummyDev has a 'bus' child to a DummyBus
 177 * DummyBus has a 'backend' child to a DummyBackend
 178 * DummyDev has a 'backend' link to DummyBackend
 179 *
 180 * When DummyDev is finalized, it unparents the
 181 * DummyBackend, which unparents the DummyDev
 182 * which deletes the 'backend' link from DummyDev
 183 * to DummyBackend. This illustrates that the
 184 * object_property_del_all() method needs to
 185 * cope with the list of properties being changed
 186 * while it iterates over them.
 187 */
 188typedef struct DummyDev DummyDev;
 189typedef struct DummyDevClass DummyDevClass;
 190typedef struct DummyBus DummyBus;
 191typedef struct DummyBusClass DummyBusClass;
 192typedef struct DummyBackend DummyBackend;
 193typedef struct DummyBackendClass DummyBackendClass;
 194
 195#define TYPE_DUMMY_DEV "qemu-dummy-dev"
 196#define TYPE_DUMMY_BUS "qemu-dummy-bus"
 197#define TYPE_DUMMY_BACKEND "qemu-dummy-backend"
 198
 199DECLARE_INSTANCE_CHECKER(DummyDev, DUMMY_DEV,
 200                         TYPE_DUMMY_DEV)
 201DECLARE_INSTANCE_CHECKER(DummyBus, DUMMY_BUS,
 202                         TYPE_DUMMY_BUS)
 203DECLARE_INSTANCE_CHECKER(DummyBackend, DUMMY_BACKEND,
 204                         TYPE_DUMMY_BACKEND)
 205
 206struct DummyDev {
 207    Object parent_obj;
 208
 209    DummyBus *bus;
 210};
 211
 212struct DummyDevClass {
 213    ObjectClass parent_class;
 214};
 215
 216struct DummyBus {
 217    Object parent_obj;
 218
 219    DummyBackend *backend;
 220};
 221
 222struct DummyBusClass {
 223    ObjectClass parent_class;
 224};
 225
 226struct DummyBackend {
 227    Object parent_obj;
 228};
 229
 230struct DummyBackendClass {
 231    ObjectClass parent_class;
 232};
 233
 234
 235static void dummy_dev_finalize(Object *obj)
 236{
 237    DummyDev *dev = DUMMY_DEV(obj);
 238
 239    object_unref(OBJECT(dev->bus));
 240}
 241
 242static void dummy_dev_init(Object *obj)
 243{
 244    DummyDev *dev = DUMMY_DEV(obj);
 245    DummyBus *bus = DUMMY_BUS(object_new(TYPE_DUMMY_BUS));
 246    DummyBackend *backend = DUMMY_BACKEND(object_new(TYPE_DUMMY_BACKEND));
 247
 248    object_property_add_child(obj, "bus", OBJECT(bus));
 249    dev->bus = bus;
 250    object_property_add_child(OBJECT(bus), "backend", OBJECT(backend));
 251    bus->backend = backend;
 252
 253    object_property_add_link(obj, "backend", TYPE_DUMMY_BACKEND,
 254                             (Object **)&bus->backend, NULL, 0);
 255}
 256
 257static void dummy_dev_unparent(Object *obj)
 258{
 259    DummyDev *dev = DUMMY_DEV(obj);
 260    object_unparent(OBJECT(dev->bus));
 261}
 262
 263static void dummy_dev_class_init(ObjectClass *klass, void *opaque)
 264{
 265    klass->unparent = dummy_dev_unparent;
 266}
 267
 268
 269static void dummy_bus_finalize(Object *obj)
 270{
 271    DummyBus *bus = DUMMY_BUS(obj);
 272
 273    object_unref(OBJECT(bus->backend));
 274}
 275
 276static void dummy_bus_init(Object *obj)
 277{
 278}
 279
 280static void dummy_bus_unparent(Object *obj)
 281{
 282    DummyBus *bus = DUMMY_BUS(obj);
 283    object_property_del(obj->parent, "backend");
 284    object_unparent(OBJECT(bus->backend));
 285}
 286
 287static void dummy_bus_class_init(ObjectClass *klass, void *opaque)
 288{
 289    klass->unparent = dummy_bus_unparent;
 290}
 291
 292static void dummy_backend_init(Object *obj)
 293{
 294}
 295
 296
 297static const TypeInfo dummy_dev_info = {
 298    .name          = TYPE_DUMMY_DEV,
 299    .parent        = TYPE_OBJECT,
 300    .instance_size = sizeof(DummyDev),
 301    .instance_init = dummy_dev_init,
 302    .instance_finalize = dummy_dev_finalize,
 303    .class_size = sizeof(DummyDevClass),
 304    .class_init = dummy_dev_class_init,
 305};
 306
 307static const TypeInfo dummy_bus_info = {
 308    .name          = TYPE_DUMMY_BUS,
 309    .parent        = TYPE_OBJECT,
 310    .instance_size = sizeof(DummyBus),
 311    .instance_init = dummy_bus_init,
 312    .instance_finalize = dummy_bus_finalize,
 313    .class_size = sizeof(DummyBusClass),
 314    .class_init = dummy_bus_class_init,
 315};
 316
 317static const TypeInfo dummy_backend_info = {
 318    .name          = TYPE_DUMMY_BACKEND,
 319    .parent        = TYPE_OBJECT,
 320    .instance_size = sizeof(DummyBackend),
 321    .instance_init = dummy_backend_init,
 322    .class_size = sizeof(DummyBackendClass),
 323};
 324
 325static QemuOptsList qemu_object_opts = {
 326    .name = "object",
 327    .implied_opt_name = "qom-type",
 328    .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
 329    .desc = {
 330        { }
 331    },
 332};
 333
 334
 335static void test_dummy_createv(void)
 336{
 337    Error *err = NULL;
 338    Object *parent = object_get_objects_root();
 339    DummyObject *dobj = DUMMY_OBJECT(
 340        object_new_with_props(TYPE_DUMMY,
 341                              parent,
 342                              "dummy0",
 343                              &err,
 344                              "bv", "yes",
 345                              "sv", "Hiss hiss hiss",
 346                              "av", "platypus",
 347                              NULL));
 348
 349    g_assert(err == NULL);
 350    g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
 351    g_assert(dobj->bv == true);
 352    g_assert(dobj->av == DUMMY_PLATYPUS);
 353
 354    g_assert(object_resolve_path_component(parent, "dummy0")
 355             == OBJECT(dobj));
 356
 357    object_unparent(OBJECT(dobj));
 358}
 359
 360
 361static Object *new_helper(Error **errp,
 362                          Object *parent,
 363                          ...)
 364{
 365    va_list vargs;
 366    Object *obj;
 367
 368    va_start(vargs, parent);
 369    obj = object_new_with_propv(TYPE_DUMMY,
 370                                parent,
 371                                "dummy0",
 372                                errp,
 373                                vargs);
 374    va_end(vargs);
 375    return obj;
 376}
 377
 378static void test_dummy_createlist(void)
 379{
 380    Error *err = NULL;
 381    Object *parent = object_get_objects_root();
 382    DummyObject *dobj = DUMMY_OBJECT(
 383        new_helper(&err,
 384                   parent,
 385                   "bv", "yes",
 386                   "sv", "Hiss hiss hiss",
 387                   "av", "platypus",
 388                   NULL));
 389
 390    g_assert(err == NULL);
 391    g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
 392    g_assert(dobj->bv == true);
 393    g_assert(dobj->av == DUMMY_PLATYPUS);
 394
 395    g_assert(object_resolve_path_component(parent, "dummy0")
 396             == OBJECT(dobj));
 397
 398    object_unparent(OBJECT(dobj));
 399}
 400
 401static void test_dummy_createcmdl(void)
 402{
 403    QemuOpts *opts;
 404    DummyObject *dobj;
 405    Error *err = NULL;
 406    const char *params = TYPE_DUMMY \
 407                         ",id=dev0," \
 408                         "bv=yes,sv=Hiss hiss hiss,av=platypus";
 409
 410    qemu_add_opts(&qemu_object_opts);
 411    opts = qemu_opts_parse(&qemu_object_opts, params, true, &err);
 412    g_assert(err == NULL);
 413    g_assert(opts);
 414
 415    dobj = DUMMY_OBJECT(user_creatable_add_opts(opts, &err));
 416    g_assert(err == NULL);
 417    g_assert(dobj);
 418    g_assert_cmpstr(dobj->sv, ==, "Hiss hiss hiss");
 419    g_assert(dobj->bv == true);
 420    g_assert(dobj->av == DUMMY_PLATYPUS);
 421
 422    user_creatable_del("dev0", &error_abort);
 423
 424    object_unref(OBJECT(dobj));
 425
 426    /*
 427     * cmdline-parsing via qemu_opts_parse() results in a QemuOpts entry
 428     * corresponding to the Object's ID to be added to the QemuOptsList
 429     * for objects. To avoid having this entry conflict with future
 430     * Objects using the same ID (which can happen in cases where
 431     * qemu_opts_parse() is used to parse the object params, such as
 432     * with hmp_object_add() at the time of this comment), we need to
 433     * check for this in user_creatable_del() and remove the QemuOpts if
 434     * it is present.
 435     *
 436     * The below check ensures this works as expected.
 437     */
 438    g_assert_null(qemu_opts_find(&qemu_object_opts, "dev0"));
 439}
 440
 441static void test_dummy_badenum(void)
 442{
 443    Error *err = NULL;
 444    Object *parent = object_get_objects_root();
 445    Object *dobj =
 446        object_new_with_props(TYPE_DUMMY,
 447                              parent,
 448                              "dummy0",
 449                              &err,
 450                              "bv", "yes",
 451                              "sv", "Hiss hiss hiss",
 452                              "av", "yeti",
 453                              NULL);
 454
 455    g_assert(dobj == NULL);
 456    g_assert(err != NULL);
 457    g_assert_cmpstr(error_get_pretty(err), ==,
 458                    "Invalid parameter 'yeti'");
 459
 460    g_assert(object_resolve_path_component(parent, "dummy0")
 461             == NULL);
 462
 463    error_free(err);
 464}
 465
 466
 467static void test_dummy_getenum(void)
 468{
 469    Error *err = NULL;
 470    int val;
 471    Object *parent = object_get_objects_root();
 472    DummyObject *dobj = DUMMY_OBJECT(
 473        object_new_with_props(TYPE_DUMMY,
 474                         parent,
 475                         "dummy0",
 476                         &err,
 477                         "av", "platypus",
 478                         NULL));
 479
 480    g_assert(err == NULL);
 481    g_assert(dobj->av == DUMMY_PLATYPUS);
 482
 483    val = object_property_get_enum(OBJECT(dobj),
 484                                   "av",
 485                                   "DummyAnimal",
 486                                   &error_abort);
 487    g_assert(val == DUMMY_PLATYPUS);
 488
 489    /* A bad enum type name */
 490    val = object_property_get_enum(OBJECT(dobj),
 491                                   "av",
 492                                   "BadAnimal",
 493                                   &err);
 494    g_assert(val == -1);
 495    error_free_or_abort(&err);
 496
 497    /* A non-enum property name */
 498    val = object_property_get_enum(OBJECT(dobj),
 499                                   "iv",
 500                                   "DummyAnimal",
 501                                   &err);
 502    g_assert(val == -1);
 503    error_free_or_abort(&err);
 504
 505    object_unparent(OBJECT(dobj));
 506}
 507
 508
 509static void test_dummy_prop_iterator(ObjectPropertyIterator *iter,
 510                                     const char *expected[], int n)
 511{
 512    ObjectProperty *prop;
 513    int i;
 514
 515    while ((prop = object_property_iter_next(iter))) {
 516        for (i = 0; i < n; i++) {
 517            if (!g_strcmp0(prop->name, expected[i])) {
 518                break;
 519            }
 520        }
 521        g_assert(i < n);
 522        expected[i] = NULL;
 523    }
 524
 525    for (i = 0; i < n; i++) {
 526        g_assert(!expected[i]);
 527    }
 528}
 529
 530static void test_dummy_iterator(void)
 531{
 532    const char *expected[] = {
 533        "type",                 /* inherited from TYPE_OBJECT */
 534        "sv", "av",             /* class properties */
 535        "bv"};                  /* instance property */
 536    Object *parent = object_get_objects_root();
 537    DummyObject *dobj = DUMMY_OBJECT(
 538        object_new_with_props(TYPE_DUMMY,
 539                              parent,
 540                              "dummy0",
 541                              &error_abort,
 542                              "bv", "yes",
 543                              "sv", "Hiss hiss hiss",
 544                              "av", "platypus",
 545                              NULL));
 546    ObjectPropertyIterator iter;
 547
 548    object_property_iter_init(&iter, OBJECT(dobj));
 549    test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
 550    object_unparent(OBJECT(dobj));
 551}
 552
 553static void test_dummy_class_iterator(void)
 554{
 555    const char *expected[] = { "type", "av", "sv" };
 556    ObjectPropertyIterator iter;
 557    ObjectClass *klass = object_class_by_name(TYPE_DUMMY);
 558
 559    object_class_property_iter_init(&iter, klass);
 560    test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
 561}
 562
 563static 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
 576static 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);
 592    object_unref(obj1);
 593    object_property_add_child(cont1, "obj2", obj2a);
 594    object_unref(obj2a);
 595    object_property_add_child(root,  "obj2", obj2b);
 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
 617int 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/class_iterator", test_dummy_class_iterator);
 634    g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
 635    g_test_add_func("/qom/resolve/partial", test_qom_partial_path);
 636
 637    return g_test_run();
 638}
 639