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