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    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
 199#define DUMMY_DEV(obj)                               \
 200    OBJECT_CHECK(DummyDev, (obj), TYPE_DUMMY_DEV)
 201#define DUMMY_BUS(obj)                               \
 202    OBJECT_CHECK(DummyBus, (obj), TYPE_DUMMY_BUS)
 203#define DUMMY_BACKEND(obj)                               \
 204    OBJECT_CHECK(DummyBackend, (obj), 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    error_free_or_abort(&err);
 495
 496    /* A non-enum property name */
 497    val = object_property_get_enum(OBJECT(dobj),
 498                                   "iv",
 499                                   "DummyAnimal",
 500                                   &err);
 501    error_free_or_abort(&err);
 502
 503    object_unparent(OBJECT(dobj));
 504}
 505
 506
 507static void test_dummy_prop_iterator(ObjectPropertyIterator *iter,
 508                                     const char *expected[], int n)
 509{
 510    ObjectProperty *prop;
 511    int i;
 512
 513    while ((prop = object_property_iter_next(iter))) {
 514        for (i = 0; i < n; i++) {
 515            if (!g_strcmp0(prop->name, expected[i])) {
 516                break;
 517            }
 518        }
 519        g_assert(i < n);
 520        expected[i] = NULL;
 521    }
 522
 523    for (i = 0; i < n; i++) {
 524        g_assert(!expected[i]);
 525    }
 526}
 527
 528static void test_dummy_iterator(void)
 529{
 530    const char *expected[] = {
 531        "type",                 /* inherited from TYPE_OBJECT */
 532        "sv", "av",             /* class properties */
 533        "bv"};                  /* instance property */
 534    Object *parent = object_get_objects_root();
 535    DummyObject *dobj = DUMMY_OBJECT(
 536        object_new_with_props(TYPE_DUMMY,
 537                              parent,
 538                              "dummy0",
 539                              &error_abort,
 540                              "bv", "yes",
 541                              "sv", "Hiss hiss hiss",
 542                              "av", "platypus",
 543                              NULL));
 544    ObjectPropertyIterator iter;
 545
 546    object_property_iter_init(&iter, OBJECT(dobj));
 547    test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
 548    object_unparent(OBJECT(dobj));
 549}
 550
 551static void test_dummy_class_iterator(void)
 552{
 553    const char *expected[] = { "type", "av", "sv" };
 554    ObjectPropertyIterator iter;
 555    ObjectClass *klass = object_class_by_name(TYPE_DUMMY);
 556
 557    object_class_property_iter_init(&iter, klass);
 558    test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
 559}
 560
 561static void test_dummy_delchild(void)
 562{
 563    Object *parent = object_get_objects_root();
 564    DummyDev *dev = DUMMY_DEV(
 565        object_new_with_props(TYPE_DUMMY_DEV,
 566                              parent,
 567                              "dev0",
 568                              &error_abort,
 569                              NULL));
 570
 571    object_unparent(OBJECT(dev));
 572}
 573
 574static void test_qom_partial_path(void)
 575{
 576    Object *root  = object_get_objects_root();
 577    Object *cont1 = container_get(root, "/cont1");
 578    Object *obj1  = object_new(TYPE_DUMMY);
 579    Object *obj2a = object_new(TYPE_DUMMY);
 580    Object *obj2b = object_new(TYPE_DUMMY);
 581    bool ambiguous;
 582
 583    /* Objects created:
 584     * /cont1
 585     * /cont1/obj1
 586     * /cont1/obj2 (obj2a)
 587     * /obj2 (obj2b)
 588     */
 589    object_property_add_child(cont1, "obj1", obj1);
 590    object_unref(obj1);
 591    object_property_add_child(cont1, "obj2", obj2a);
 592    object_unref(obj2a);
 593    object_property_add_child(root,  "obj2", obj2b);
 594    object_unref(obj2b);
 595
 596    ambiguous = false;
 597    g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous));
 598    g_assert(ambiguous);
 599    g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL));
 600
 601    ambiguous = false;
 602    g_assert(!object_resolve_path("obj2", &ambiguous));
 603    g_assert(ambiguous);
 604    g_assert(!object_resolve_path("obj2", NULL));
 605
 606    ambiguous = false;
 607    g_assert(object_resolve_path("obj1", &ambiguous) == obj1);
 608    g_assert(!ambiguous);
 609    g_assert(object_resolve_path("obj1", NULL) == obj1);
 610
 611    object_unparent(obj2b);
 612    object_unparent(cont1);
 613}
 614
 615int main(int argc, char **argv)
 616{
 617    g_test_init(&argc, &argv, NULL);
 618
 619    module_call_init(MODULE_INIT_QOM);
 620    type_register_static(&dummy_info);
 621    type_register_static(&dummy_dev_info);
 622    type_register_static(&dummy_bus_info);
 623    type_register_static(&dummy_backend_info);
 624
 625    g_test_add_func("/qom/proplist/createlist", test_dummy_createlist);
 626    g_test_add_func("/qom/proplist/createv", test_dummy_createv);
 627    g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl);
 628    g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
 629    g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
 630    g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
 631    g_test_add_func("/qom/proplist/class_iterator", test_dummy_class_iterator);
 632    g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
 633    g_test_add_func("/qom/resolve/partial", test_qom_partial_path);
 634
 635    return g_test_run();
 636}
 637