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