/* Add @value to the current QObject being built.
 * If the stack is visiting a dictionary or list, @value is now owned
 * by that container. Otherwise, @value is now the root.  */
static void qobject_output_add_obj(QObjectOutputVisitor *qov, const char *name,
                                   QObject *value)
{
    QStackEntry *e = QSLIST_FIRST(&qov->stack);
    QObject *cur = e ? e->value : NULL;

    if (!cur) {
        /* Don't allow reuse of visitor on more than one root */
        assert(!qov->root);
        qov->root = value;
    } else {
        switch (qobject_type(cur)) {
        case QTYPE_QDICT:
            assert(name);
            qdict_put_obj(qobject_to(QDict, cur), name, value);
            break;
        case QTYPE_QLIST:
            assert(!name);
            qlist_append_obj(qobject_to(QList, cur), value);
            break;
        default:
            g_assert_not_reached();
        }
    }
}
Exemple #2
0
/* test commands that involve both input parameters and return values */
static void test_dispatch_cmd_io(void)
{
    QDict *req = qdict_new();
    QDict *args = qdict_new();
    QDict *args3 = qdict_new();
    QDict *ud1a = qdict_new();
    QDict *ud1b = qdict_new();
    QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef;
    QDict *ret_dict_dict2, *ret_dict_dict2_userdef;
    QNum *ret3;
    int64_t val;

    qdict_put_int(ud1a, "integer", 42);
    qdict_put_str(ud1a, "string", "hello");
    qdict_put_int(ud1b, "integer", 422);
    qdict_put_str(ud1b, "string", "hello2");
    qdict_put(args, "ud1a", ud1a);
    qdict_put(args, "ud1b", ud1b);
    qdict_put(req, "arguments", args);
    qdict_put_str(req, "execute", "user_def_cmd2");

    ret = qobject_to(QDict, test_qmp_dispatch(req));

    assert(!strcmp(qdict_get_str(ret, "string0"), "blah1"));
    ret_dict = qdict_get_qdict(ret, "dict1");
    assert(!strcmp(qdict_get_str(ret_dict, "string1"), "blah2"));
    ret_dict_dict = qdict_get_qdict(ret_dict, "dict2");
    ret_dict_dict_userdef = qdict_get_qdict(ret_dict_dict, "userdef");
    assert(qdict_get_int(ret_dict_dict_userdef, "integer") == 42);
    assert(!strcmp(qdict_get_str(ret_dict_dict_userdef, "string"), "hello"));
    assert(!strcmp(qdict_get_str(ret_dict_dict, "string"), "blah3"));
    ret_dict_dict2 = qdict_get_qdict(ret_dict, "dict3");
    ret_dict_dict2_userdef = qdict_get_qdict(ret_dict_dict2, "userdef");
    assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422);
    assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2"));
    assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4"));
    qobject_unref(ret);

    qdict_put_int(args3, "a", 66);
    qdict_put(req, "arguments", args3);
    qdict_put_str(req, "execute", "guest-get-time");

    ret3 = qobject_to(QNum, test_qmp_dispatch(req));
    g_assert(qnum_get_try_int(ret3, &val));
    g_assert_cmpint(val, ==, 66);
    qobject_unref(ret3);

    qobject_unref(req);
}
Exemple #3
0
QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
                    bool allow_oob)
{
    Error *err = NULL;
    QDict *dict = qobject_to(QDict, request);
    QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL;
    QDict *rsp;

    ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
    if (err) {
        rsp = qmp_error_response(err);
    } else if (ret) {
        rsp = qdict_new();
        qdict_put_obj(rsp, "return", ret);
    } else {
        /* Can only happen for commands with QCO_NO_SUCCESS_RESP */
        rsp = NULL;
    }

    if (rsp && id) {
        qdict_put_obj(rsp, "id", qobject_ref(id));
    }

    return rsp;
}
Exemple #4
0
static bool qom_get_bool(const char *path, const char *prop)
{
    QBool *value = qobject_to(QBool, qom_get(path, prop));
    bool b = qbool_get_bool(value);

    qobject_unref(value);
    return b;
}
Exemple #5
0
static QString *from_json_str(const char *jstr, bool single, Error **errp)
{
    char quote = single ? '\'' : '"';
    char *qjstr = g_strdup_printf("%c%s%c", quote, jstr, quote);
    QString *ret = qobject_to(QString, qobject_from_json(qjstr, errp));

    g_free(qjstr);
    return ret;
}
Exemple #6
0
/**
 * qnum_is_equal(): Test whether the two QNums are equal
 *
 * Negative integers are never considered equal to unsigned integers,
 * but positive integers in the range [0, INT64_MAX] are considered
 * equal independently of whether the QNum's kind is i64 or u64.
 *
 * Doubles are never considered equal to integers.
 */
bool qnum_is_equal(const QObject *x, const QObject *y)
{
    QNum *num_x = qobject_to(QNum, x);
    QNum *num_y = qobject_to(QNum, y);

    switch (num_x->kind) {
    case QNUM_I64:
        switch (num_y->kind) {
        case QNUM_I64:
            /* Comparison in native int64_t type */
            return num_x->u.i64 == num_y->u.i64;
        case QNUM_U64:
            /* Implicit conversion of x to uin64_t, so we have to
             * check its sign before */
            return num_x->u.i64 >= 0 && num_x->u.i64 == num_y->u.u64;
        case QNUM_DOUBLE:
            return false;
        }
        abort();
    case QNUM_U64:
        switch (num_y->kind) {
        case QNUM_I64:
            return qnum_is_equal(y, x);
        case QNUM_U64:
            /* Comparison in native uint64_t type */
            return num_x->u.u64 == num_y->u.u64;
        case QNUM_DOUBLE:
            return false;
        }
        abort();
    case QNUM_DOUBLE:
        switch (num_y->kind) {
        case QNUM_I64:
        case QNUM_U64:
            return false;
        case QNUM_DOUBLE:
            /* Comparison in native double type */
            return num_x->u.dbl == num_y->u.dbl;
        }
        abort();
    }

    abort();
}
Exemple #7
0
static void test_qga_get_vcpus(gconstpointer fix)
{
    const TestFixture *fixture = fix;
    QDict *ret;
    QList *list;
    const QListEntry *entry;

    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-vcpus'}");
    g_assert_nonnull(ret);
    qmp_assert_no_error(ret);

    /* check there is at least a cpu */
    list = qdict_get_qlist(ret, "return");
    entry = qlist_first(list);
    g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online"));
    g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id"));

    qobject_unref(ret);
}
Exemple #8
0
static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
                                     Error **errp)
{
    const char *exec_key = NULL;
    const QDictEntry *ent;
    const char *arg_name;
    const QObject *arg_obj;
    QDict *dict;

    dict = qobject_to(QDict, request);
    if (!dict) {
        error_setg(errp, "QMP input must be a JSON object");
        return NULL;
    }

    for (ent = qdict_first(dict); ent;
         ent = qdict_next(dict, ent)) {
        arg_name = qdict_entry_key(ent);
        arg_obj = qdict_entry_value(ent);

        if (!strcmp(arg_name, "execute")
            || (!strcmp(arg_name, "exec-oob") && allow_oob)) {
            if (qobject_type(arg_obj) != QTYPE_QSTRING) {
                error_setg(errp, "QMP input member '%s' must be a string",
                           arg_name);
                return NULL;
            }
            if (exec_key) {
                error_setg(errp, "QMP input member '%s' clashes with '%s'",
                           arg_name, exec_key);
                return NULL;
            }
            exec_key = arg_name;
        } else if (!strcmp(arg_name, "arguments")) {
            if (qobject_type(arg_obj) != QTYPE_QDICT) {
                error_setg(errp,
                           "QMP input member 'arguments' must be an object");
                return NULL;
            }
        } else if (!strcmp(arg_name, "id")) {
            continue;
        } else {
            error_setg(errp, "QMP input member '%s' is unexpected",
                       arg_name);
            return NULL;
        }
    }

    if (!exec_key) {
        error_setg(errp, "QMP input lacks member 'execute'");
        return NULL;
    }

    return dict;
}
Exemple #9
0
static void qdict_crumple_test_empty(void)
{
    QDict *src, *dst;

    src = qdict_new();

    dst = qobject_to(QDict, qdict_crumple(src, &error_abort));

    g_assert_cmpint(qdict_size(dst), ==, 0);

    qobject_unref(src);
    qobject_unref(dst);
}
Exemple #10
0
static void qlit_equal_qobject_test(void)
{
    QObject *qobj = make_qobject();

    g_assert(qlit_equal_qobject(&qlit, qobj));

    g_assert(!qlit_equal_qobject(&qlit_foo, qobj));

    qdict_put(qobject_to(QDict, qobj), "bee", qlist_new());
    g_assert(!qlit_equal_qobject(&qlit, qobj));

    qobject_unref(qobj);
}
Exemple #11
0
static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
                                 Error **errp)
{
    QList *keypairs;
    QString *name;
    QString *value;
    const char *key;
    size_t remaining;
    int ret = 0;

    if (!keypairs_json) {
        return ret;
    }
    keypairs = qobject_to(QList,
                          qobject_from_json(keypairs_json, &error_abort));
    remaining = qlist_size(keypairs) / 2;
    assert(remaining);

    while (remaining--) {
        name = qobject_to(QString, qlist_pop(keypairs));
        value = qobject_to(QString, qlist_pop(keypairs));
        assert(name && value);
        key = qstring_get_str(name);

        ret = rados_conf_set(cluster, key, qstring_get_str(value));
        qobject_unref(value);
        if (ret < 0) {
            error_setg_errno(errp, -ret, "invalid conf option %s", key);
            qobject_unref(name);
            ret = -EINVAL;
            break;
        }
        qobject_unref(name);
    }

    qobject_unref(keypairs);
    return ret;
}
Exemple #12
0
static void test_qga_get_fsinfo(gconstpointer fix)
{
    const TestFixture *fixture = fix;
    QDict *ret;
    QList *list;
    const QListEntry *entry;

    ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-fsinfo'}");
    g_assert_nonnull(ret);
    qmp_assert_no_error(ret);

    /* sanity-check the response if there are any filesystems */
    list = qdict_get_qlist(ret, "return");
    entry = qlist_first(list);
    if (entry) {
        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name"));
        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "mountpoint"));
        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "type"));
        g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk"));
    }

    qobject_unref(ret);
}
Exemple #13
0
static void qobject_from_qlit_test(void)
{
    QObject *obj, *qobj = qobject_from_qlit(&qlit);
    QDict *qdict;
    QList *bee;

    qdict = qobject_to(QDict, qobj);
    g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42);
    g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world");
    g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL);

    bee = qdict_get_qlist(qdict, "bee");
    obj = qlist_pop(bee);
    g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43);
    qobject_unref(obj);
    obj = qlist_pop(bee);
    g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44);
    qobject_unref(obj);
    obj = qlist_pop(bee);
    g_assert(qbool_get_bool(qobject_to(QBool, obj)));
    qobject_unref(obj);

    qobject_unref(qobj);
}
Exemple #14
0
static char *get_cpu0_qom_path(void)
{
    QDict *resp;
    QList *ret;
    QDict *cpu0;
    char *path;

    resp = qmp("{'execute': 'query-cpus', 'arguments': {}}");
    g_assert(qdict_haskey(resp, "return"));
    ret = qdict_get_qlist(resp, "return");

    cpu0 = qobject_to(QDict, qlist_peek(ret));
    path = g_strdup(qdict_get_str(cpu0, "qom_path"));
    qobject_unref(resp);
    return path;
}
Exemple #15
0
/* handle requests/control events coming in over the channel */
static void process_event(JSONMessageParser *parser, GQueue *tokens)
{
    GAState *s = container_of(parser, GAState, parser);
    QDict *qdict;
    Error *err = NULL;
    int ret;

    g_assert(s && parser);

    g_debug("process_event: called");
    qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err));
    if (err || !qdict) {
        qobject_unref(qdict);
        qdict = qdict_new();
        if (!err) {
            g_warning("failed to parse event: unknown error");
            error_setg(&err, QERR_JSON_PARSING);
        } else {
            g_warning("failed to parse event: %s", error_get_pretty(err));
        }
        qdict_put_obj(qdict, "error", qmp_build_error_object(err));
        error_free(err);
    }

    /* handle host->guest commands */
    if (qdict_haskey(qdict, "execute")) {
        process_command(s, qdict);
    } else {
        if (!qdict_haskey(qdict, "error")) {
            qobject_unref(qdict);
            qdict = qdict_new();
            g_warning("unrecognized payload format");
            error_setg(&err, QERR_UNSUPPORTED);
            qdict_put_obj(qdict, "error", qmp_build_error_object(err));
            error_free(err);
        }
        ret = send_response(s, QOBJECT(qdict));
        if (ret < 0) {
            g_warning("error sending error response: %s", strerror(-ret));
        }
    }

    qobject_unref(qdict);
}
Exemple #16
0
static QAuthZ *
qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
{
    GError *err = NULL;
    gchar *content = NULL;
    gsize len;
    QObject *obj = NULL;
    QDict *pdict;
    Visitor *v = NULL;
    QAuthZ *ret = NULL;

    trace_qauthz_list_file_load(fauthz, fauthz->filename);
    if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) {
        error_setg(errp, "Unable to read '%s': %s",
                   fauthz->filename, err->message);
        goto cleanup;
    }

    obj = qobject_from_json(content, errp);
    if (!obj) {
        goto cleanup;
    }

    pdict = qobject_to(QDict, obj);
    if (!pdict) {
        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict");
        goto cleanup;
    }

    v = qobject_input_visitor_new(obj);

    ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST,
                                            NULL, pdict, v, errp);

 cleanup:
    visit_free(v);
    qobject_unref(obj);
    if (err) {
        g_error_free(err);
    }
    g_free(content);
    return ret;
}
Exemple #17
0
/* handle requests/control events coming in over the channel */
static void process_event(JSONMessageParser *parser, GQueue *tokens)
{
    GAState *s = container_of(parser, GAState, parser);
    QObject *obj;
    QDict *req, *rsp;
    Error *err = NULL;
    int ret;

    g_assert(s && parser);

    g_debug("process_event: called");
    obj = json_parser_parse_err(tokens, NULL, &err);
    if (err) {
        goto err;
    }
    req = qobject_to(QDict, obj);
    if (!req) {
        error_setg(&err, QERR_JSON_PARSING);
        goto err;
    }
    if (!qdict_haskey(req, "execute")) {
        g_warning("unrecognized payload format");
        error_setg(&err, QERR_UNSUPPORTED);
        goto err;
    }

    process_command(s, req);
    qobject_unref(obj);
    return;

err:
    g_warning("failed to parse event: %s", error_get_pretty(err));
    rsp = qmp_error_response(err);
    ret = send_response(s, rsp);
    if (ret < 0) {
        g_warning("error sending error response: %s", strerror(-ret));
    }
    qobject_unref(rsp);
    qobject_unref(obj);
}
Exemple #18
0
/**
 * qnum_destroy_obj(): Free all memory allocated by a
 * QNum object
 */
void qnum_destroy_obj(QObject *obj)
{
    assert(obj != NULL);
    g_free(qobject_to(QNum, obj));
}
Exemple #19
0
static void qdict_crumple_test_recursive(void)
{
    QDict *src, *dst, *rule, *vnc, *acl, *listen;
    QDict *empty, *empty_dict, *empty_list_0;
    QList *rules, *empty_list, *empty_dict_a;

    src = qdict_new();
    qdict_put_str(src, "vnc.listen.addr", "127.0.0.1");
    qdict_put_str(src, "vnc.listen.port", "5901");
    qdict_put_str(src, "vnc.acl.rules.0.match", "fred");
    qdict_put_str(src, "vnc.acl.rules.0.policy", "allow");
    qdict_put_str(src, "vnc.acl.rules.1.match", "bob");
    qdict_put_str(src, "vnc.acl.rules.1.policy", "deny");
    qdict_put_str(src, "vnc.acl.default", "deny");
    qdict_put_str(src, "vnc.acl..name", "acl0");
    qdict_put_str(src, "vnc.acl.rule..name", "acl0");
    qdict_put(src, "empty.dict.a", qlist_new());
    qdict_put(src, "empty.list.0", qdict_new());

    dst = qobject_to(QDict, qdict_crumple(src, &error_abort));
    g_assert(dst);
    g_assert_cmpint(qdict_size(dst), ==, 2);

    vnc = qdict_get_qdict(dst, "vnc");
    g_assert(vnc);
    g_assert_cmpint(qdict_size(vnc), ==, 3);

    listen = qdict_get_qdict(vnc, "listen");
    g_assert(listen);
    g_assert_cmpint(qdict_size(listen), ==, 2);
    g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr"));
    g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port"));

    acl = qdict_get_qdict(vnc, "acl");
    g_assert(acl);
    g_assert_cmpint(qdict_size(acl), ==, 3);

    rules = qdict_get_qlist(acl, "rules");
    g_assert(rules);
    g_assert_cmpint(qlist_size(rules), ==, 2);

    rule = qobject_to(QDict, qlist_pop(rules));
    g_assert(rule);
    g_assert_cmpint(qdict_size(rule), ==, 2);
    g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match"));
    g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy"));
    qobject_unref(rule);

    rule = qobject_to(QDict, qlist_pop(rules));
    g_assert(rule);
    g_assert_cmpint(qdict_size(rule), ==, 2);
    g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match"));
    g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy"));
    qobject_unref(rule);

    /* With recursive crumpling, we should see all names unescaped */
    g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name"));
    g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name"));

    empty = qdict_get_qdict(dst, "empty");
    g_assert(empty);
    g_assert_cmpint(qdict_size(empty), ==, 2);
    empty_dict = qdict_get_qdict(empty, "dict");
    g_assert(empty_dict);
    g_assert_cmpint(qdict_size(empty_dict), ==, 1);
    empty_dict_a = qdict_get_qlist(empty_dict, "a");
    g_assert(empty_dict_a && qlist_empty(empty_dict_a));
    empty_list = qdict_get_qlist(empty, "list");
    g_assert(empty_list);
    g_assert_cmpint(qlist_size(empty_list), ==, 1);
    empty_list_0 = qobject_to(QDict, qlist_pop(empty_list));
    g_assert(empty_list_0);
    g_assert_cmpint(qdict_size(empty_list_0), ==, 0);

    qobject_unref(src);
    qobject_unref(dst);
}
Exemple #20
0
static void qobject_is_equal_dict_test(void)
{
    Error *local_err = NULL;
    QDict *dict_0, *dict_1, *dict_cloned;
    QDict *dict_different_key, *dict_different_value, *dict_different_null_key;
    QDict *dict_longer, *dict_shorter, *dict_nested;
    QDict *dict_crumpled;

    dict_0 = qdict_new();
    dict_1 = qdict_new();
    dict_different_key = qdict_new();
    dict_different_value = qdict_new();
    dict_different_null_key = qdict_new();
    dict_longer = qdict_new();
    dict_shorter = qdict_new();
    dict_nested = qdict_new();

    qdict_put_int(dict_0, "f.o", 1);
    qdict_put_int(dict_0, "bar", 2);
    qdict_put_int(dict_0, "baz", 3);
    qdict_put_null(dict_0, "null");

    qdict_put_int(dict_1, "f.o", 1);
    qdict_put_int(dict_1, "bar", 2);
    qdict_put_int(dict_1, "baz", 3);
    qdict_put_null(dict_1, "null");

    qdict_put_int(dict_different_key, "F.o", 1);
    qdict_put_int(dict_different_key, "bar", 2);
    qdict_put_int(dict_different_key, "baz", 3);
    qdict_put_null(dict_different_key, "null");

    qdict_put_int(dict_different_value, "f.o", 42);
    qdict_put_int(dict_different_value, "bar", 2);
    qdict_put_int(dict_different_value, "baz", 3);
    qdict_put_null(dict_different_value, "null");

    qdict_put_int(dict_different_null_key, "f.o", 1);
    qdict_put_int(dict_different_null_key, "bar", 2);
    qdict_put_int(dict_different_null_key, "baz", 3);
    qdict_put_null(dict_different_null_key, "none");

    qdict_put_int(dict_longer, "f.o", 1);
    qdict_put_int(dict_longer, "bar", 2);
    qdict_put_int(dict_longer, "baz", 3);
    qdict_put_int(dict_longer, "xyz", 4);
    qdict_put_null(dict_longer, "null");

    qdict_put_int(dict_shorter, "f.o", 1);
    qdict_put_int(dict_shorter, "bar", 2);
    qdict_put_int(dict_shorter, "baz", 3);

    qdict_put(dict_nested, "f", qdict_new());
    qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1);
    qdict_put_int(dict_nested, "bar", 2);
    qdict_put_int(dict_nested, "baz", 3);
    qdict_put_null(dict_nested, "null");

    dict_cloned = qdict_clone_shallow(dict_0);

    check_equal(dict_0, dict_1, dict_cloned);
    check_unequal(dict_0, dict_different_key, dict_different_value,
                  dict_different_null_key, dict_longer, dict_shorter,
                  dict_nested);

    dict_crumpled = qobject_to(QDict, qdict_crumple(dict_1, &local_err));
    g_assert(!local_err);
    check_equal(dict_crumpled, dict_nested);

    qdict_flatten(dict_nested);
    check_equal(dict_0, dict_nested);

    /* Containing an NaN value will make this dict compare unequal to
     * itself */
    qdict_put(dict_0, "NaN", qnum_from_double(NAN));
    g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false);

    free_all(dict_0, dict_1, dict_cloned, dict_different_key,
             dict_different_value, dict_different_null_key, dict_longer,
             dict_shorter, dict_nested, dict_crumpled);
}
Exemple #21
0
static void qdict_array_split_test(void)
{
    QDict *test_dict = qdict_new();
    QDict *dict1, *dict2;
    QNum *int1;
    QList *test_list;

    /*
     * Test the split of
     *
     * {
     *     "1.x": 0,
     *     "4.y": 1,
     *     "0.a": 42,
     *     "o.o": 7,
     *     "0.b": 23,
     *     "2": 66
     * }
     *
     * to
     *
     * [
     *     {
     *         "a": 42,
     *         "b": 23
     *     },
     *     {
     *         "x": 0
     *     },
     *     66
     * ]
     *
     * and
     *
     * {
     *     "4.y": 1,
     *     "o.o": 7
     * }
     *
     * (remaining in the old QDict)
     *
     * This example is given in the comment of qdict_array_split().
     */

    qdict_put_int(test_dict, "1.x", 0);
    qdict_put_int(test_dict, "4.y", 1);
    qdict_put_int(test_dict, "0.a", 42);
    qdict_put_int(test_dict, "o.o", 7);
    qdict_put_int(test_dict, "0.b", 23);
    qdict_put_int(test_dict, "2", 66);

    qdict_array_split(test_dict, &test_list);

    dict1 = qobject_to(QDict, qlist_pop(test_list));
    dict2 = qobject_to(QDict, qlist_pop(test_list));
    int1 = qobject_to(QNum, qlist_pop(test_list));

    g_assert(dict1);
    g_assert(dict2);
    g_assert(int1);
    g_assert(qlist_empty(test_list));

    qobject_unref(test_list);

    g_assert(qdict_get_int(dict1, "a") == 42);
    g_assert(qdict_get_int(dict1, "b") == 23);

    g_assert(qdict_size(dict1) == 2);

    qobject_unref(dict1);

    g_assert(qdict_get_int(dict2, "x") == 0);

    g_assert(qdict_size(dict2) == 1);

    qobject_unref(dict2);

    g_assert_cmpint(qnum_get_int(int1), ==, 66);

    qobject_unref(int1);

    g_assert(qdict_get_int(test_dict, "4.y") == 1);
    g_assert(qdict_get_int(test_dict, "o.o") == 7);

    g_assert(qdict_size(test_dict) == 2);

    qobject_unref(test_dict);

    /*
     * Test the split of
     *
     * {
     *     "0": 42,
     *     "1": 23,
     *     "1.x": 84
     * }
     *
     * to
     *
     * [
     *     42
     * ]
     *
     * and
     *
     * {
     *     "1": 23,
     *     "1.x": 84
     * }
     *
     * That is, test whether splitting stops if there is both an entry with key
     * of "%u" and other entries with keys prefixed "%u." for the same index.
     */

    test_dict = qdict_new();

    qdict_put_int(test_dict, "0", 42);
    qdict_put_int(test_dict, "1", 23);
    qdict_put_int(test_dict, "1.x", 84);

    qdict_array_split(test_dict, &test_list);

    int1 = qobject_to(QNum, qlist_pop(test_list));

    g_assert(int1);
    g_assert(qlist_empty(test_list));

    qobject_unref(test_list);

    g_assert_cmpint(qnum_get_int(int1), ==, 42);

    qobject_unref(int1);

    g_assert(qdict_get_int(test_dict, "1") == 23);
    g_assert(qdict_get_int(test_dict, "1.x") == 84);

    g_assert(qdict_size(test_dict) == 2);

    qobject_unref(test_dict);
}