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