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", &err);
 423    g_assert(err == NULL);
 424    error_free(err);
 425
 426    object_unref(OBJECT(dobj));
 427
 428    /*
 429     * cmdline-parsing via qemu_opts_parse() results in a QemuOpts entry
 430     * corresponding to the Object's ID to be added to the QemuOptsList
 431     * for objects. To avoid having this entry conflict with future
 432     * Objects using the same ID (which can happen in cases where
 433     * qemu_opts_parse() is used to parse the object params, such as
 434     * with hmp_object_add() at the time of this comment), we need to
 435     * check for this in user_creatable_del() and remove the QemuOpts if
 436     * it is present.
 437     *
 438     * The below check ensures this works as expected.
 439     */
 440    g_assert_null(qemu_opts_find(&qemu_object_opts, "dev0"));
 441}
 442
 443static void test_dummy_badenum(void)
 444{
 445    Error *err = NULL;
 446    Object *parent = object_get_objects_root();
 447    Object *dobj =
 448        object_new_with_props(TYPE_DUMMY,
 449                              parent,
 450                              "dummy0",
 451                              &err,
 452                              "bv", "yes",
 453                              "sv", "Hiss hiss hiss",
 454                              "av", "yeti",
 455                              NULL);
 456
 457    g_assert(dobj == NULL);
 458    g_assert(err != NULL);
 459    g_assert_cmpstr(error_get_pretty(err), ==,
 460                    "Invalid parameter 'yeti'");
 461
 462    g_assert(object_resolve_path_component(parent, "dummy0")
 463             == NULL);
 464
 465    error_free(err);
 466}
 467
 468
 469static void test_dummy_getenum(void)
 470{
 471    Error *err = NULL;
 472    int val;
 473    Object *parent = object_get_objects_root();
 474    DummyObject *dobj = DUMMY_OBJECT(
 475        object_new_with_props(TYPE_DUMMY,
 476                         parent,
 477                         "dummy0",
 478                         &err,
 479                         "av", "platypus",
 480                         NULL));
 481
 482    g_assert(err == NULL);
 483    g_assert(dobj->av == DUMMY_PLATYPUS);
 484
 485    val = object_property_get_enum(OBJECT(dobj),
 486                                   "av",
 487                                   "DummyAnimal",
 488                                   &err);
 489    g_assert(err == NULL);
 490    g_assert(val == DUMMY_PLATYPUS);
 491
 492    /* A bad enum type name */
 493    val = object_property_get_enum(OBJECT(dobj),
 494                                   "av",
 495                                   "BadAnimal",
 496                                   &err);
 497    g_assert(err != NULL);
 498    error_free(err);
 499    err = NULL;
 500
 501    /* A non-enum property name */
 502    val = object_property_get_enum(OBJECT(dobj),
 503                                   "iv",
 504                                   "DummyAnimal",
 505                                   &err);
 506    g_assert(err != NULL);
 507    error_free(err);
 508
 509    object_unparent(OBJECT(dobj));
 510}
 511
 512
 513static void test_dummy_prop_iterator(ObjectPropertyIterator *iter,
 514                                     const char *expected[], int n)
 515{
 516    ObjectProperty *prop;
 517    int i;
 518
 519    while ((prop = object_property_iter_next(iter))) {
 520        for (i = 0; i < n; i++) {
 521            if (!g_strcmp0(prop->name, expected[i])) {
 522                break;
 523            }
 524        }
 525        g_assert(i < n);
 526        expected[i] = NULL;
 527    }
 528
 529    for (i = 0; i < n; i++) {
 530        g_assert(!expected[i]);
 531    }
 532}
 533
 534static void test_dummy_iterator(void)
 535{
 536    const char *expected[] = {
 537        "type",                 /* inherited from TYPE_OBJECT */
 538        "sv", "av",             /* class properties */
 539        "bv"};                  /* instance property */
 540    Object *parent = object_get_objects_root();
 541    DummyObject *dobj = DUMMY_OBJECT(
 542        object_new_with_props(TYPE_DUMMY,
 543                              parent,
 544                              "dummy0",
 545                              &error_abort,
 546                              "bv", "yes",
 547                              "sv", "Hiss hiss hiss",
 548                              "av", "platypus",
 549                              NULL));
 550    ObjectPropertyIterator iter;
 551
 552    object_property_iter_init(&iter, OBJECT(dobj));
 553    test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
 554    object_unparent(OBJECT(dobj));
 555}
 556
 557static void test_dummy_class_iterator(void)
 558{
 559    const char *expected[] = { "type", "av", "sv" };
 560    ObjectPropertyIterator iter;
 561    ObjectClass *klass = object_class_by_name(TYPE_DUMMY);
 562
 563    object_class_property_iter_init(&iter, klass);
 564    test_dummy_prop_iterator(&iter, expected, ARRAY_SIZE(expected));
 565}
 566
 567static void test_dummy_delchild(void)
 568{
 569    Object *parent = object_get_objects_root();
 570    DummyDev *dev = DUMMY_DEV(
 571        object_new_with_props(TYPE_DUMMY_DEV,
 572                              parent,
 573                              "dev0",
 574                              &error_abort,
 575                              NULL));
 576
 577    object_unparent(OBJECT(dev));
 578}
 579
 580static void test_qom_partial_path(void)
 581{
 582    Object *root  = object_get_objects_root();
 583    Object *cont1 = container_get(root, "/cont1");
 584    Object *obj1  = object_new(TYPE_DUMMY);
 585    Object *obj2a = object_new(TYPE_DUMMY);
 586    Object *obj2b = object_new(TYPE_DUMMY);
 587    bool ambiguous;
 588
 589    /* Objects created:
 590     * /cont1
 591     * /cont1/obj1
 592     * /cont1/obj2 (obj2a)
 593     * /obj2 (obj2b)
 594     */
 595    object_property_add_child(cont1, "obj1", obj1);
 596    object_unref(obj1);
 597    object_property_add_child(cont1, "obj2", obj2a);
 598    object_unref(obj2a);
 599    object_property_add_child(root,  "obj2", obj2b);
 600    object_unref(obj2b);
 601
 602    ambiguous = false;
 603    g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous));
 604    g_assert(ambiguous);
 605    g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL));
 606
 607    ambiguous = false;
 608    g_assert(!object_resolve_path("obj2", &ambiguous));
 609    g_assert(ambiguous);
 610    g_assert(!object_resolve_path("obj2", NULL));
 611
 612    ambiguous = false;
 613    g_assert(object_resolve_path("obj1", &ambiguous) == obj1);
 614    g_assert(!ambiguous);
 615    g_assert(object_resolve_path("obj1", NULL) == obj1);
 616
 617    object_unparent(obj2b);
 618    object_unparent(cont1);
 619}
 620
 621int main(int argc, char **argv)
 622{
 623    g_test_init(&argc, &argv, NULL);
 624
 625    module_call_init(MODULE_INIT_QOM);
 626    type_register_static(&dummy_info);
 627    type_register_static(&dummy_dev_info);
 628    type_register_static(&dummy_bus_info);
 629    type_register_static(&dummy_backend_info);
 630
 631    g_test_add_func("/qom/proplist/createlist", test_dummy_createlist);
 632    g_test_add_func("/qom/proplist/createv", test_dummy_createv);
 633    g_test_add_func("/qom/proplist/createcmdline", test_dummy_createcmdl);
 634    g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
 635    g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
 636    g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
 637    g_test_add_func("/qom/proplist/class_iterator", test_dummy_class_iterator);
 638    g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
 639    g_test_add_func("/qom/resolve/partial", test_qom_partial_path);
 640
 641    return g_test_run();
 642}
 643